Add Windows Implementation Libraries

This commit is contained in:
Silent 2019-11-01 00:09:52 +01:00
parent e00459f68f
commit f52a0aba24
No known key found for this signature in database
GPG key ID: AE53149BB0C45AF1
69 changed files with 61643 additions and 0 deletions

2
Externals/WIL/.gitattributes vendored Normal file
View file

@ -0,0 +1,2 @@
# Disable CRLF-mapping for all files in the depot.
* -text

336
Externals/WIL/.gitignore vendored Normal file
View file

@ -0,0 +1,336 @@
## 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/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.*
# 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
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.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
# Visual Studio Trace Files
*.e2e
# 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
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# 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
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/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
# 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/
# 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
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# 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
# 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/
# Visual Studio Code directory
.vscode/
# CMake/Build output
build/

21
Externals/WIL/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,21 @@
cmake_minimum_required(VERSION 3.11)
project(WIL)
# Set by build server to speed up build/reduce file/object size
option(FAST_BUILD "Sets options to speed up build/reduce obj/executable size" OFF)
if (NOT DEFINED WIL_BUILD_VERSION)
set(WIL_BUILD_VERSION "0.0.0")
endif()
# Detect the Windows SDK version. If we're using the Visual Studio generator, this will be provided for us. Otherwise
# we'll need to assume that this value comes from the command line (e.g. through the VS command prompt)
if (DEFINED CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION)
set(WIL_WINDOWS_SDK_VERSION ${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION})
else()
# This has a trailing backslash for whatever reason...
string(REGEX REPLACE "\\\\$" "" WIL_WINDOWS_SDK_VERSION "$ENV{WindowsSDKVersion}")
endif()
add_subdirectory(packaging)
add_subdirectory(tests)

3
Externals/WIL/CODE_OF_CONDUCT.md vendored Normal file
View file

@ -0,0 +1,3 @@
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

21
Externals/WIL/LICENSE vendored Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation. All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE

93
Externals/WIL/README.md vendored Normal file
View file

@ -0,0 +1,93 @@
# Windows Implementation Libraries (WIL)
[![Build Status](https://dev.azure.com/msft-wil/Windows%20Implementation%20Library/_apis/build/status/Microsoft.wil?branchName=master)](https://dev.azure.com/msft-wil/Windows%20Implementation%20Library/_build/latest?definitionId=1&branchName=master)
The Windows Implementation Libraries (WIL) is a header-only C++ library created to make life easier
for developers on Windows through readable type-safe C++ interfaces for common Windows coding patterns.
Some things that WIL includes to whet your appetite:
- [`include/wil/resource.h`](include/wil/resource.h)
([documentation](https://github.com/Microsoft/wil/wiki/RAII-resource-wrappers)):
Smart pointers and auto-releasing resource wrappers to let you manage Windows
API HANDLEs, HWNDs, and other resources and resource handles with
[RAII](https://en.cppreference.com/w/cpp/language/raii) semantics.
- [`include/wil/win32_helpers.h`](include/wil/win32_helpers.h): Wrappers for API functions
that save you the work of manually specifying buffer sizes, calling a function twice
to get the needed buffer size and then allocate and pass the right-size buffer,
casting or converting between types, and so on.
- [`include/wil/registry.h`](include/wil/registry.h): Registry watchers that can
call a lambda function or callback you provide whenever a certain tree within
the Windows registry changes.
- [`include/wil/result.h`](include/wil/result.h)
([documentation](https://github.com/Microsoft/wil/wiki/Error-handling-helpers)):
Preprocessor macros to help you check for errors from Windows API functions,
in many of the myriad ways those errors are reported, and surface them as
error codes or C++ exceptions in your code.
WIL can be used by C++ code that uses C++ exceptions as well as code that uses returned
error codes to report errors. All of WIL can be used from user-space Windows code,
and some (such as the RAII resource wrappers) can even be used in kernel mode.
# Documentation
This project is documented in [its GitHub wiki](https://github.com/Microsoft/wil/wiki). Feel free to contribute to it!
# Consuming WIL
WIL follows the "live at head" philosophy, so you should feel free to consume WIL directly from the GitHub repo however you please: as a GIT submodule, symbolic link, download and copy files, etc. and update to the latest version at your own cadence. Alternatively, WIL is available using a few package managers, mentioned below. These packages will be updated periodically, likely to average around once or twice per month.
## Consuming WIL via NuGet
WIL is available on nuget.org under the name [Microsoft.Windows.ImplementationLibrary](https://www.nuget.org/packages/Microsoft.Windows.ImplementationLibrary/). This package includes the header files under the [include](include) directory as well as a [.targets](packaging/nuget/Microsoft.Windows.ImplementationLibrary.targets) file.
## Consuming WIL via vcpkg
WIL is also available using [vcpkg](https://github.com/microsoft/vcpkg) under the name [wil](https://github.com/microsoft/vcpkg/blob/master/ports/wil/portfile.cmake). Instructions for installing packages can be found in the [vcpkg GitHub docs](https://github.com/microsoft/vcpkg/blob/master/docs/examples/installing-and-using-packages.md). In general, once vcpkg is set up on the system, you can run:
```cmd
C:\vcpkg> vcpkg install wil:x86-windows
C:\vcpkg> vcpkg install wil:x64-windows
```
Note that even though WIL is a header-only library, you still need to install the package for all architectures/platforms you wish to use it with. Otherwise, WIL won't be added to the include path for the missing architectures/platforms. Execute `vcpkg help triplet` for a list of available options.
# Building/Testing
To get started testing WIL, first make sure that you have a recent version of [Visual Studio](https://visualstudio.microsoft.com/downloads/) and the most recent [Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk) installed. If you are doing
any non-trivial work, also be sure to have a recent version of [Clang](http://releases.llvm.org/download.html) installed. Once everything is installed, open a VS
native command window (e.g. "x64 Native Tools Command Prompt for VS 2019"). If you are familiar with CMake you can get started building normally. Otherwise, or if you prefer to skip all of the boilerplate, you can use one of the scripts in the [scripts](scripts) directory:
```cmd
C:\wil> scripts\init.cmd -c clang -g ninja -b debug
```
You can execute `init.cmd --help` for a summary of available options. The scripts use a common directory pattern of `build/$(compiler)$(arch)$(type)` for the build output root. E.g. `build/clang64debug` when using Clang as the compiler, x64 as the architecture, and Debug as the build type. It is this directory where you will want to build from. For example, if you initialized using the command above, you can build the tests like so:
```cmd
C:\wil\build\clang64debug> ninja
```
Or, if you want to only build a single test (e.g. for improved compile times):
```cmd
C:\wil\build\clang64debug> ninja witest.noexcept
```
If you initialized using MSBuild as the generator, there will be a `.sln` file in the root of the build directory. You
can either open the solution in Visual Studio or invoke MSBuild directly to build.
The output is a number of test executables. If you used the initialization script(s) mentioned above, or if you followed
the same directory naming convention of those scripts, you can use the [runtests.cmd](scripts/runtests.cmd) script,
which will execute any test executables that have been built, erroring out - and preserving the exit code - if any test
fails. Note that MSBuild will modify the output directory names, so this script is only compatible with using Ninja as the
generator. If you are at the tail end of of a change, you can execute the following to get a wide range of coverage:
```cmd
C:\wil> scripts\init_all.cmd
C:\wil> scripts\build_all.cmd
C:\wil> scripts\runtests.cmd
```
Note that this will only test for the architecture that corresponds to the command window you opened. You will want to
repeat this process for the other architecture (e.g. by using the "x86 Native Tools Command Prompt for VS 2019")
# Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

60
Externals/WIL/ThirdPartyNotices.txt vendored Normal file
View file

@ -0,0 +1,60 @@
THIRD PARTY SOFTWARE NOTICES AND INFORMATION
Do Not Translate or Localize
This software incorporates material from third parties. Microsoft makes certain open source code available at http://3rdpartysource.microsoft.com, or you may send a check or money order for US $5.00, including the product name, the open source component name, and version number, to:
Source Code Compliance Team
Microsoft Corporation
One Microsoft Way
Redmond, WA 98052
USA
Notwithstanding any other terms, you may reverse engineer this software to the extent required to debug changes to any libraries licensed under the GNU Lesser General Public License.
Libc++
Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
Catch2
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,69 @@
# E.g. replace_cxx_flag("/W[0-4]", "/W4")
macro(replace_cxx_flag pattern text)
foreach (flag
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
string(REGEX REPLACE "${pattern}" "${text}" ${flag} "${${flag}}")
endforeach()
endmacro()
macro(append_cxx_flag text)
foreach (flag
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
string(APPEND ${flag} " ${text}")
endforeach()
endmacro()
# Fixup default compiler settings
# Be as strict as reasonably possible, since we want to support consumers using strict warning levels
replace_cxx_flag("/W[0-4]" "/W4")
append_cxx_flag("/WX")
# We want to be as conformant as possible, so tell MSVC to not be permissive (note that this has no effect on clang-cl)
append_cxx_flag("/permissive-")
# wistd::function has padding due to alignment. This is expected
append_cxx_flag("/wd4324")
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
# Ignore a few Clang warnings. We may want to revisit in the future to see if any of these can/should be removed
append_cxx_flag("-Wno-switch")
append_cxx_flag("-Wno-c++17-compat-mangling")
append_cxx_flag("-Wno-missing-field-initializers")
# For tests, we want to be able to test self assignment, so disable this warning
append_cxx_flag("-Wno-self-assign-overloaded")
append_cxx_flag("-Wno-self-move")
# clang-cl does not understand the /permissive- flag (or at least it opts to ignore it). We can achieve similar
# results through the following flags.
append_cxx_flag("-fno-delayed-template-parsing")
# NOTE: Windows headers not clean enough for us to realistically attempt to start fixing these errors yet. That
# said, errors that originate from WIL headers may benefit
# append_cxx_flag("-fno-ms-compatibility")
# append_cxx_flag("-ferror-limit=999")
# append_cxx_flag("-fmacro-backtrace-limit=0")
# -fno-ms-compatibility turns off preprocessor compatability, which currently only works when __VA_OPT__ support is
# available (i.e. >= C++20)
# append_cxx_flag("-Xclang -std=c++2a")
else()
# Flags that are either ignored or unrecognized by clang-cl
# TODO: https://github.com/Microsoft/wil/issues/6
# append_cxx_flag("/experimental:preprocessor")
# CRT headers are not yet /experimental:preprocessor clean, so work around the known issues
# append_cxx_flag("/Wv:18")
append_cxx_flag("/bigobj")
# NOTE: Temporary workaround while https://github.com/microsoft/wil/issues/102 is being investigated
append_cxx_flag("/d2FH4-")
endif()

2832
Externals/WIL/include/wil/com.h vendored Normal file

File diff suppressed because it is too large Load diff

747
Externals/WIL/include/wil/common.h vendored Normal file
View file

@ -0,0 +1,747 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_COMMON_INCLUDED
#define __WIL_COMMON_INCLUDED
#if defined(_KERNEL_MODE ) && !defined(__WIL_MIN_KERNEL)
// This define indicates that the WIL usage is in a kernel mode context where
// a high degree of WIL functionality is desired.
//
// Use (sparingly) to change behavior based on whether WIL is being used in kernel
// mode or user mode.
#define WIL_KERNEL_MODE
#endif
// Defining WIL_HIDE_DEPRECATED will hide everything deprecated.
// Each wave of deprecation will add a new WIL_HIDE_DEPRECATED_YYMM number that can be used to lock deprecation at
// a particular point, allowing components to avoid backslide and catch up to the current independently.
#ifdef WIL_HIDE_DEPRECATED
#define WIL_HIDE_DEPRECATED_1809
#endif
#ifdef WIL_HIDE_DEPRECATED_1809
#define WIL_HIDE_DEPRECATED_1612
#endif
#ifdef WIL_HIDE_DEPRECATED_1612
#define WIL_HIDE_DEPRECATED_1611
#endif
// Implementation side note: ideally the deprecation would be done with the function-level declspec
// as it allows you to utter the error text when used. The declspec works, but doing it selectively with
// a macro makes intellisense deprecation comments not work. So we just use the #pragma deprecation.
#ifdef WIL_WARN_DEPRECATED
#define WIL_WARN_DEPRECATED_1809
#endif
#ifdef WIL_WARN_DEPRECATED_1809
#define WIL_WARN_DEPRECATED_1612
#endif
#ifdef WIL_WARN_DEPRECATED_1612
#define WIL_WARN_DEPRECATED_1611
#endif
#ifdef WIL_WARN_DEPRECATED_1809
#define WIL_WARN_DEPRECATED_1809_PRAGMA(...) __pragma(deprecated(__VA_ARGS__))
#else
#define WIL_WARN_DEPRECATED_1809_PRAGMA(...)
#endif
#ifdef WIL_WARN_DEPRECATED_1611
#define WIL_WARN_DEPRECATED_1611_PRAGMA(...) __pragma(deprecated(__VA_ARGS__))
#else
#define WIL_WARN_DEPRECATED_1611_PRAGMA(...)
#endif
#ifdef WIL_WARN_DEPRECATED_1612
#define WIL_WARN_DEPRECATED_1612_PRAGMA(...) __pragma(deprecated(__VA_ARGS__))
#else
#define WIL_WARN_DEPRECATED_1612_PRAGMA(...)
#endif
#if defined(_MSVC_LANG)
#define __WI_SUPPRESS_4127_S __pragma(warning(push)) __pragma(warning(disable:4127)) __pragma(warning(disable:26498)) __pragma(warning(disable:4245))
#define __WI_SUPPRESS_4127_E __pragma(warning(pop))
#define __WI_SUPPRESS_NULLPTR_ANALYSIS __pragma(warning(suppress:28285)) __pragma(warning(suppress:6504))
#define __WI_SUPPRESS_NONINIT_ANALYSIS __pragma(warning(suppress:26495))
#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS __pragma(warning(suppress:26439))
#else
#define __WI_SUPPRESS_4127_S
#define __WI_SUPPRESS_4127_E
#define __WI_SUPPRESS_NULLPTR_ANALYSIS
#define __WI_SUPPRESS_NONINIT_ANALYSIS
#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS
#endif
#if !defined(__cplusplus) || defined(__WIL_MIN_KERNEL)
#define WI_ODR_PRAGMA(NAME, TOKEN)
#define WI_NOEXCEPT
#else
#pragma warning(push)
#pragma warning(disable:4714) // __forceinline not honored
// DO NOT add *any* further includes to this file -- there should be no dependencies from its usage
#include <sal.h>
#include "wistd_type_traits.h"
//! This macro inserts ODR violation protection; the macro allows it to be compatible with straight "C" code
#define WI_ODR_PRAGMA(NAME, TOKEN) __pragma(detect_mismatch("ODR_violation_" NAME "_mismatch", TOKEN))
#ifdef WIL_KERNEL_MODE
WI_ODR_PRAGMA("WIL_KERNEL_MODE", "1")
#else
WI_ODR_PRAGMA("WIL_KERNEL_MODE", "0")
#endif
// Some SAL remapping / decoration to better support Doxygen. Macros that look like function calls can
// confuse Doxygen when they are used to decorate a function or variable. We simplify some of these to
// basic macros without the function for common use cases.
/// @cond
#define _Success_return_ _Success_(return)
#define _Success_true_ _Success_(true)
#define __declspec_noinline_ __declspec(noinline)
#define __declspec_selectany_ __declspec(selectany)
/// @endcond
#if defined(_CPPUNWIND) && !defined(WIL_SUPPRESS_EXCEPTIONS)
/** This define is automatically set when exceptions are enabled within wil.
It is automatically defined when your code is compiled with exceptions enabled (via checking for the built-in
_CPPUNWIND flag) unless you explicitly define WIL_SUPPRESS_EXCEPTIONS ahead of including your first wil
header. All exception-based WIL methods and classes are included behind:
~~~~
#ifdef WIL_ENABLE_EXCEPTIONS
// code
#endif
~~~~
This enables exception-free code to directly include WIL headers without worrying about exception-based
routines suddenly becoming available. */
#define WIL_ENABLE_EXCEPTIONS
#endif
/// @endcond
/// @cond
#if defined(WIL_EXCEPTION_MODE)
static_assert(WIL_EXCEPTION_MODE <= 2, "Invalid exception mode");
#elif !defined(WIL_LOCK_EXCEPTION_MODE)
#define WIL_EXCEPTION_MODE 0 // default, can link exception-based and non-exception based libraries together
#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "0")
#elif defined(WIL_ENABLE_EXCEPTIONS)
#define WIL_EXCEPTION_MODE 1 // new code optimization: ONLY support linking libraries together that have exceptions enabled
#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "1")
#else
#define WIL_EXCEPTION_MODE 2 // old code optimization: ONLY support linking libraries that are NOT using exceptions
#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "2")
#endif
#if WIL_EXCEPTION_MODE == 1 && !defined(WIL_ENABLE_EXCEPTIONS)
#error Must enable exceptions when WIL_EXCEPTION_MODE == 1
#endif
// block for documentation only
#if defined(WIL_DOXYGEN)
/** This define can be explicitly set to disable exception usage within wil.
Normally this define is never needed as the WIL_ENABLE_EXCEPTIONS macro is enabled automatically by looking
at _CPPUNWIND. If your code compiles with exceptions enabled, but does not want to enable the exception-based
classes and methods from WIL, define this macro ahead of including the first WIL header. */
#define WIL_SUPPRESS_EXCEPTIONS
/** This define can be explicitly set to lock the process exception mode to WIL_ENABLE_EXCEPTIONS.
Locking the exception mode provides optimizations to exception barriers, staging hooks and DLL load costs as it eliminates the need to
do copy-on-write initialization of various function pointers and the necessary indirection that's done within WIL to avoid ODR violations
when linking libraries together with different exception handling semantics. */
#define WIL_LOCK_EXCEPTION_MODE
/** This define explicit sets the exception mode for the process to control optimizations.
Three exception modes are available:
0) This is the default. This enables a binary to link both exception-based and non-exception based libraries together that
use WIL. This adds overhead to exception barriers, DLL copy on write pages and indirection through function pointers to avoid ODR
violations when linking libraries together with different exception handling semantics.
1) Prefer this setting when it can be used. This locks the binary to only supporting libraries which were built with exceptions enabled.
2) This locks the binary to libraries built without exceptions. */
#define WIL_EXCEPTION_MODE
#endif
#if (__cplusplus >= 201703) || (_MSVC_LANG >= 201703)
#define WIL_HAS_CXX_17 1
#else
#define WIL_HAS_CXX_17 0
#endif
// Until we'll have C++17 enabled in our code base, we're falling back to SAL
#define WI_NODISCARD __WI_LIBCPP_NODISCARD_ATTRIBUTE
//! @defgroup macrobuilding Macro Composition
//! The following macros are building blocks primarily intended for authoring other macros.
//! @{
//! Re-state a macro value (indirection for composition)
#define WI_FLATTEN(...) __VA_ARGS__
/// @cond
#define __WI_PASTE_imp(a, b) a##b
/// @endcond
//! This macro is for use in other macros to paste two tokens together, such as a constant and the __LINE__ macro.
#define WI_PASTE(a, b) __WI_PASTE_imp(a, b)
/// @cond
#define __WI_HAS_VA_OPT_IMPL(F, T, ...) T
#define __WI_HAS_VA_OPT_(...) __WI_HAS_VA_OPT_IMPL(__VA_OPT__(0,) 1, 0)
/// @endcond
//! Evaluates to '1' when support for '__VA_OPT__' is available, else '0'
#define WI_HAS_VA_OPT __WI_HAS_VA_OPT_(unused)
/// @cond
#define __WI_ARGS_COUNT1(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, \
A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, \
A60, A61, A62, A63, A64, A65, A66, A67, A68, A69, A70, A71, A72, A73, A74, A75, A76, A77, A78, A79, A80, A81, A82, A83, A84, A85, A86, A87, A88, A89, \
A90, A91, A92, A93, A94, A95, A96, A97, A98, A99, count, ...) count
#define __WI_ARGS_COUNT0(...) WI_FLATTEN(__WI_ARGS_COUNT1(__VA_ARGS__, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, \
79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \
39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
#define __WI_ARGS_COUNT_PREFIX(...) 0, __VA_ARGS__
/// @endcond
//! This variadic macro returns the number of arguments passed to it (up to 99).
#if WI_HAS_VA_OPT
#define WI_ARGS_COUNT(...) __WI_ARGS_COUNT0(0 __VA_OPT__(, __VA_ARGS__))
#else
#define WI_ARGS_COUNT(...) __WI_ARGS_COUNT0(__WI_ARGS_COUNT_PREFIX(__VA_ARGS__))
#endif
/// @cond
#define __WI_FOR_imp0( fn)
#define __WI_FOR_imp1( fn, arg) fn(arg)
#define __WI_FOR_imp2( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp1(fn, __VA_ARGS__))
#define __WI_FOR_imp3( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp2(fn, __VA_ARGS__))
#define __WI_FOR_imp4( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp3(fn, __VA_ARGS__))
#define __WI_FOR_imp5( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp4(fn, __VA_ARGS__))
#define __WI_FOR_imp6( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp5(fn, __VA_ARGS__))
#define __WI_FOR_imp7( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp6(fn, __VA_ARGS__))
#define __WI_FOR_imp8( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp7(fn, __VA_ARGS__))
#define __WI_FOR_imp9( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp8(fn, __VA_ARGS__))
#define __WI_FOR_imp10(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp9(fn, __VA_ARGS__))
#define __WI_FOR_imp11(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp10(fn, __VA_ARGS__))
#define __WI_FOR_imp12(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp11(fn, __VA_ARGS__))
#define __WI_FOR_imp13(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp12(fn, __VA_ARGS__))
#define __WI_FOR_imp14(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp13(fn, __VA_ARGS__))
#define __WI_FOR_imp15(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp14(fn, __VA_ARGS__))
#define __WI_FOR_imp16(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp15(fn, __VA_ARGS__))
#define __WI_FOR_imp17(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp16(fn, __VA_ARGS__))
#define __WI_FOR_imp18(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp17(fn, __VA_ARGS__))
#define __WI_FOR_imp19(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp18(fn, __VA_ARGS__))
#define __WI_FOR_imp20(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp19(fn, __VA_ARGS__))
#define __WI_FOR_imp21(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp20(fn, __VA_ARGS__))
#define __WI_FOR_imp22(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp21(fn, __VA_ARGS__))
#define __WI_FOR_imp23(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp22(fn, __VA_ARGS__))
#define __WI_FOR_imp24(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp23(fn, __VA_ARGS__))
#define __WI_FOR_imp25(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp24(fn, __VA_ARGS__))
#define __WI_FOR_imp26(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp25(fn, __VA_ARGS__))
#define __WI_FOR_imp27(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp26(fn, __VA_ARGS__))
#define __WI_FOR_imp28(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp27(fn, __VA_ARGS__))
#define __WI_FOR_imp29(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp28(fn, __VA_ARGS__))
#define __WI_FOR_imp30(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp29(fn, __VA_ARGS__))
#define __WI_FOR_imp31(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp30(fn, __VA_ARGS__))
#define __WI_FOR_imp32(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp31(fn, __VA_ARGS__))
#define __WI_FOR_imp33(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp32(fn, __VA_ARGS__))
#define __WI_FOR_imp34(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp33(fn, __VA_ARGS__))
#define __WI_FOR_imp35(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp34(fn, __VA_ARGS__))
#define __WI_FOR_imp36(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp35(fn, __VA_ARGS__))
#define __WI_FOR_imp37(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp36(fn, __VA_ARGS__))
#define __WI_FOR_imp38(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp37(fn, __VA_ARGS__))
#define __WI_FOR_imp39(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp38(fn, __VA_ARGS__))
#define __WI_FOR_imp40(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp39(fn, __VA_ARGS__))
#define __WI_FOR_imp41(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp40(fn, __VA_ARGS__))
#define __WI_FOR_imp42(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp41(fn, __VA_ARGS__))
#define __WI_FOR_imp43(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp42(fn, __VA_ARGS__))
#define __WI_FOR_imp44(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp43(fn, __VA_ARGS__))
#define __WI_FOR_imp45(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp44(fn, __VA_ARGS__))
#define __WI_FOR_imp46(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp45(fn, __VA_ARGS__))
#define __WI_FOR_imp47(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp46(fn, __VA_ARGS__))
#define __WI_FOR_imp48(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp47(fn, __VA_ARGS__))
#define __WI_FOR_imp49(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp48(fn, __VA_ARGS__))
#define __WI_FOR_imp50(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp49(fn, __VA_ARGS__))
#define __WI_FOR_imp51(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp50(fn, __VA_ARGS__))
#define __WI_FOR_imp52(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp51(fn, __VA_ARGS__))
#define __WI_FOR_imp53(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp52(fn, __VA_ARGS__))
#define __WI_FOR_imp54(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp53(fn, __VA_ARGS__))
#define __WI_FOR_imp55(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp54(fn, __VA_ARGS__))
#define __WI_FOR_imp56(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp55(fn, __VA_ARGS__))
#define __WI_FOR_imp57(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp56(fn, __VA_ARGS__))
#define __WI_FOR_imp58(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp57(fn, __VA_ARGS__))
#define __WI_FOR_imp59(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp58(fn, __VA_ARGS__))
#define __WI_FOR_imp60(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp59(fn, __VA_ARGS__))
#define __WI_FOR_imp61(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp60(fn, __VA_ARGS__))
#define __WI_FOR_imp62(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp61(fn, __VA_ARGS__))
#define __WI_FOR_imp63(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp62(fn, __VA_ARGS__))
#define __WI_FOR_imp64(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp63(fn, __VA_ARGS__))
#define __WI_FOR_imp65(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp64(fn, __VA_ARGS__))
#define __WI_FOR_imp66(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp65(fn, __VA_ARGS__))
#define __WI_FOR_imp67(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp66(fn, __VA_ARGS__))
#define __WI_FOR_imp68(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp67(fn, __VA_ARGS__))
#define __WI_FOR_imp69(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp68(fn, __VA_ARGS__))
#define __WI_FOR_imp70(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp69(fn, __VA_ARGS__))
#define __WI_FOR_imp71(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp70(fn, __VA_ARGS__))
#define __WI_FOR_imp72(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp71(fn, __VA_ARGS__))
#define __WI_FOR_imp73(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp72(fn, __VA_ARGS__))
#define __WI_FOR_imp74(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp73(fn, __VA_ARGS__))
#define __WI_FOR_imp75(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp74(fn, __VA_ARGS__))
#define __WI_FOR_imp76(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp75(fn, __VA_ARGS__))
#define __WI_FOR_imp77(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp76(fn, __VA_ARGS__))
#define __WI_FOR_imp78(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp77(fn, __VA_ARGS__))
#define __WI_FOR_imp79(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp78(fn, __VA_ARGS__))
#define __WI_FOR_imp80(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp79(fn, __VA_ARGS__))
#define __WI_FOR_imp81(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp80(fn, __VA_ARGS__))
#define __WI_FOR_imp82(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp81(fn, __VA_ARGS__))
#define __WI_FOR_imp83(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp82(fn, __VA_ARGS__))
#define __WI_FOR_imp84(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp83(fn, __VA_ARGS__))
#define __WI_FOR_imp85(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp84(fn, __VA_ARGS__))
#define __WI_FOR_imp86(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp85(fn, __VA_ARGS__))
#define __WI_FOR_imp87(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp86(fn, __VA_ARGS__))
#define __WI_FOR_imp88(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp87(fn, __VA_ARGS__))
#define __WI_FOR_imp89(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp88(fn, __VA_ARGS__))
#define __WI_FOR_imp90(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp89(fn, __VA_ARGS__))
#define __WI_FOR_imp91(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp90(fn, __VA_ARGS__))
#define __WI_FOR_imp92(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp91(fn, __VA_ARGS__))
#define __WI_FOR_imp93(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp92(fn, __VA_ARGS__))
#define __WI_FOR_imp94(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp93(fn, __VA_ARGS__))
#define __WI_FOR_imp95(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp94(fn, __VA_ARGS__))
#define __WI_FOR_imp96(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp95(fn, __VA_ARGS__))
#define __WI_FOR_imp97(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp96(fn, __VA_ARGS__))
#define __WI_FOR_imp98(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp97(fn, __VA_ARGS__))
#define __WI_FOR_imp99(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp98(fn, __VA_ARGS__))
#define __WI_FOR_imp(n, fnAndArgs) WI_PASTE(__WI_FOR_imp, n) fnAndArgs
/// @endcond
//! Iterates through each of the given arguments invoking the specified macro against each one.
#define WI_FOREACH(fn, ...) __WI_FOR_imp(WI_ARGS_COUNT(__VA_ARGS__), (fn, ##__VA_ARGS__))
//! Dispatches a single macro name to separate macros based on the number of arguments passed to it.
#define WI_MACRO_DISPATCH(name, ...) WI_PASTE(WI_PASTE(name, WI_ARGS_COUNT(__VA_ARGS__)), (__VA_ARGS__))
//! @} // Macro composition helpers
#define __R_ENABLE_IF_IS_CLASS(ptrType) wistd::enable_if_t<wistd::is_class<ptrType>::value, void*> = (void*)0
#define __R_ENABLE_IF_IS_NOT_CLASS(ptrType) wistd::enable_if_t<!wistd::is_class<ptrType>::value, void*> = (void*)0
//! @defgroup bitwise Bitwise Inspection and Manipulation
//! Bitwise helpers to improve readability and reduce the error rate of bitwise operations.
//! Several macros have been constructed to assist with bitwise inspection and manipulation. These macros exist
//! for two primary purposes:
//!
//! 1. To improve the readability of bitwise comparisons and manipulation.
//!
//! The macro names are the more concise, readable form of what's being done and do not require that any flags
//! or variables be specified multiple times for the comparisons.
//!
//! 2. To reduce the error rate associated with bitwise operations.
//!
//! The readability improvements naturally lend themselves to this by cutting down the number of concepts.
//! Using `WI_IsFlagSet(var, MyEnum::Flag)` rather than `((var & MyEnum::Flag) == MyEnum::Flag)` removes the comparison
//! operator and repetition in the flag value.
//!
//! Additionally, these macros separate single flag operations (which tend to be the most common) from multi-flag
//! operations so that compile-time errors are generated for bitwise operations which are likely incorrect,
//! such as: `WI_IsFlagSet(var, MyEnum::None)` or `WI_IsFlagSet(var, MyEnum::ValidMask)`.
//!
//! Note that the single flag helpers should be used when a compile-time constant single flag is being manipulated. These
//! helpers provide compile-time errors on misuse and should be preferred over the multi-flag helpers. The multi-flag helpers
//! should be used when multiple flags are being used simultaneously or when the flag values are not compile-time constants.
//!
//! Common example usage (manipulation of flag variables):
//! ~~~~
//! WI_SetFlag(m_flags, MyFlags::Foo); // Set a single flag in the given variable
//! WI_SetAllFlags(m_flags, MyFlags::Foo | MyFlags::Bar); // Set one or more flags
//! WI_ClearFlagIf(m_flags, MyFlags::Bar, isBarClosed); // Conditionally clear a single flag based upon a bool
//! WI_ClearAllFlags(m_flags, MyFlags::Foo | MyFlags::Bar); // Clear one or more flags from the given variable
//! WI_ToggleFlag(m_flags, MyFlags::Foo); // Toggle (change to the opposite value) a single flag
//! WI_UpdateFlag(m_flags, MyFlags::Bar, isBarClosed); // Sets or Clears a single flag from the given variable based upon a bool value
//! WI_UpdateFlagsInMask(m_flags, flagsMask, newFlagValues); // Sets or Clears the flags in flagsMask to the masked values from newFlagValues
//! ~~~~
//! Common example usage (inspection of flag variables):
//! ~~~~
//! if (WI_IsFlagSet(m_flags, MyFlags::Foo)) // Is a single flag set in the given variable?
//! if (WI_IsAnyFlagSet(m_flags, MyFlags::Foo | MyFlags::Bar)) // Is at least one flag from the given mask set?
//! if (WI_AreAllFlagsClear(m_flags, MyFlags::Foo | MyFlags::Bar)) // Are all flags in the given list clear?
//! if (WI_IsSingleFlagSet(m_flags)) // Is *exactly* one flag set in the given variable?
//! ~~~~
//! @{
//! Returns the unsigned type of the same width and numeric value as the given enum
#define WI_EnumValue(val) static_cast<::wil::integral_from_enum<decltype(val)>>(val)
//! Validates that exactly ONE bit is set in compile-time constant `flag`
#define WI_StaticAssertSingleBitSet(flag) static_cast<decltype(flag)>(::wil::details::verify_single_flag_helper<static_cast<unsigned long long>(WI_EnumValue(flag))>::value)
//! @name Bitwise manipulation macros
//! @{
//! Set zero or more bitflags specified by `flags` in the variable `var`.
#define WI_SetAllFlags(var, flags) ((var) |= (flags))
//! Set a single compile-time constant `flag` in the variable `var`.
#define WI_SetFlag(var, flag) WI_SetAllFlags(var, WI_StaticAssertSingleBitSet(flag))
//! Conditionally sets a single compile-time constant `flag` in the variable `var` only if `condition` is true.
#define WI_SetFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_SetFlag(var, flag); } } while ((void)0, 0)
//! Clear zero or more bitflags specified by `flags` from the variable `var`.
#define WI_ClearAllFlags(var, flags) ((var) &= ~(flags))
//! Clear a single compile-time constant `flag` from the variable `var`.
#define WI_ClearFlag(var, flag) WI_ClearAllFlags(var, WI_StaticAssertSingleBitSet(flag))
//! Conditionally clear a single compile-time constant `flag` in the variable `var` only if `condition` is true.
#define WI_ClearFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_ClearFlag(var, flag); } } while ((void)0, 0)
//! Changes a single compile-time constant `flag` in the variable `var` to be set if `isFlagSet` is true or cleared if `isFlagSet` is false.
#define WI_UpdateFlag(var, flag, isFlagSet) (wil::verify_bool(isFlagSet) ? WI_SetFlag(var, flag) : WI_ClearFlag(var, flag))
//! Changes only the flags specified by `flagsMask` in the variable `var` to match the corresponding flags in `newFlags`.
#define WI_UpdateFlagsInMask(var, flagsMask, newFlags) wil::details::UpdateFlagsInMaskHelper(var, flagsMask, newFlags)
//! Toggles (XOR the value) of multiple bitflags specified by `flags` in the variable `var`.
#define WI_ToggleAllFlags(var, flags) ((var) ^= (flags))
//! Toggles (XOR the value) of a single compile-time constant `flag` in the variable `var`.
#define WI_ToggleFlag(var, flag) WI_ToggleAllFlags(var, WI_StaticAssertSingleBitSet(flag))
//! @} // bitwise manipulation macros
//! @name Bitwise inspection macros
//! @{
//! Evaluates as true if every bitflag specified in `flags` is set within `val`.
#define WI_AreAllFlagsSet(val, flags) wil::details::AreAllFlagsSetHelper(val, flags)
//! Evaluates as true if one or more bitflags specified in `flags` are set within `val`.
#define WI_IsAnyFlagSet(val, flags) (static_cast<decltype((val) & (flags))>(WI_EnumValue(val) & WI_EnumValue(flags)) != static_cast<decltype((val) & (flags))>(0))
//! Evaluates as true if a single compile-time constant `flag` is set within `val`.
#define WI_IsFlagSet(val, flag) WI_IsAnyFlagSet(val, WI_StaticAssertSingleBitSet(flag))
//! Evaluates as true if every bitflag specified in `flags` is clear within `val`.
#define WI_AreAllFlagsClear(val, flags) (static_cast<decltype((val) & (flags))>(WI_EnumValue(val) & WI_EnumValue(flags)) == static_cast<decltype((val) & (flags))>(0))
//! Evaluates as true if one or more bitflags specified in `flags` are clear within `val`.
#define WI_IsAnyFlagClear(val, flags) (!wil::details::AreAllFlagsSetHelper(val, flags))
//! Evaluates as true if a single compile-time constant `flag` is clear within `val`.
#define WI_IsFlagClear(val, flag) WI_AreAllFlagsClear(val, WI_StaticAssertSingleBitSet(flag))
//! Evaluates as true if exactly one bit (any bit) is set within `val`.
#define WI_IsSingleFlagSet(val) wil::details::IsSingleFlagSetHelper(val)
//! Evaluates as true if exactly one bit from within the specified `mask` is set within `val`.
#define WI_IsSingleFlagSetInMask(val, mask) wil::details::IsSingleFlagSetHelper((val) & (mask))
//! Evaluates as true if exactly one bit (any bit) is set within `val` or if there are no bits set within `val`.
#define WI_IsClearOrSingleFlagSet(val) wil::details::IsClearOrSingleFlagSetHelper(val)
//! Evaluates as true if exactly one bit from within the specified `mask` is set within `val` or if there are no bits from `mask` set within `val`.
#define WI_IsClearOrSingleFlagSetInMask(val, mask) wil::details::IsClearOrSingleFlagSetHelper((val) & (mask))
//! @}
#if defined(WIL_DOXYGEN)
/** This macro provides a C++ header with a guaranteed initialization function.
Normally, were a global object's constructor used for this purpose, the optimizer/linker might throw
the object away if it's unreferenced (which throws away the side-effects that the initialization function
was trying to achieve). Using this macro forces linker inclusion of a variable that's initialized by the
provided function to elide that optimization.
//!
This functionality is primarily provided as a building block for header-based libraries (such as WIL)
to be able to layer additional functionality into other libraries by their mere inclusion. Alternative models
of initialization should be used whenever they are available.
~~~~
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
WI_HEADER_INITITALIZATION_FUNCTION(InitializeDesktopFamilyApis, []
{
g_pfnGetModuleName = GetCurrentModuleName;
g_pfnFailFastInLoaderCallout = FailFastInLoaderCallout;
return 1;
});
#endif
~~~~
The above example is used within WIL to decide whether or not the library containing WIL is allowed to use
desktop APIs. Building this functionality as #IFDEFs within functions would create ODR violations, whereas
doing it with global function pointers and header initialization allows a runtime determination. */
#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn)
#elif defined(_M_IX86)
#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) \
extern "C" { __declspec(selectany) unsigned char g_header_init_ ## name = static_cast<unsigned char>(fn()); } \
__pragma(comment(linker, "/INCLUDE:_g_header_init_" #name))
#elif defined(_M_IA64) || defined(_M_AMD64) || defined(_M_ARM) || defined(_M_ARM64)
#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) \
extern "C" { __declspec(selectany) unsigned char g_header_init_ ## name = static_cast<unsigned char>(fn()); } \
__pragma(comment(linker, "/INCLUDE:g_header_init_" #name))
#else
#error linker pragma must include g_header_init variation
#endif
/** All Windows Implementation Library classes and functions are located within the "wil" namespace.
The 'wil' namespace is an intentionally short name as the intent is for code to be able to reference
the namespace directly (example: `wil::srwlock lock;`) without a using statement. Resist adding a using
statement for wil to avoid introducing potential name collisions between wil and other namespaces. */
namespace wil
{
/// @cond
namespace details
{
template <typename T>
class pointer_range
{
public:
pointer_range(T begin_, T end_) : m_begin(begin_), m_end(end_) {}
T begin() const { return m_begin; }
T end() const { return m_end; }
private:
T m_begin;
T m_end;
};
}
/// @endcond
/** Enables using range-based for between a begin and end object pointer.
~~~~
for (auto& obj : make_range(objPointerBegin, objPointerEnd)) { }
~~~~ */
template <typename T>
details::pointer_range<T> make_range(T begin, T end)
{
return details::pointer_range<T>(begin, end);
}
/** Enables using range-based for on a range when given the base pointer and the number of objects in the range.
~~~~
for (auto& obj : make_range(objPointer, objCount)) { }
~~~~ */
template <typename T>
details::pointer_range<T> make_range(T begin, size_t count)
{
return details::pointer_range<T>(begin, begin + count);
}
//! @defgroup outparam Output Parameters
//! Improve the conciseness of assigning values to optional output parameters.
//! @{
/** Assign the given value to an optional output parameter.
Makes code more concise by removing trivial `if (outParam)` blocks. */
template <typename T>
inline void assign_to_opt_param(_Out_opt_ T *outParam, T val)
{
if (outParam != nullptr)
{
*outParam = val;
}
}
/** Assign NULL to an optional output pointer parameter.
Makes code more concise by removing trivial `if (outParam)` blocks. */
template <typename T>
inline void assign_null_to_opt_param(_Out_opt_ T *outParam)
{
if (outParam != nullptr)
{
*outParam = nullptr;
}
}
//! @} // end output parameter helpers
/** Performs a logical or of the given variadic template parameters allowing indirect compile-time boolean evaluation.
Example usage:
~~~~
template <unsigned int... Rest>
struct FeatureRequiredBy
{
static const bool enabled = wil::variadic_logical_or<WilFeature<Rest>::enabled...>::value;
};
~~~~ */
template <bool...> struct variadic_logical_or;
/// @cond
template <> struct variadic_logical_or<> : wistd::false_type { };
template <bool... Rest> struct variadic_logical_or<true, Rest...> : wistd::true_type { };
template <bool... Rest> struct variadic_logical_or<false, Rest...> : variadic_logical_or<Rest...>::type { };
/// @endcond
/// @cond
namespace details
{
template <unsigned long long flag>
struct verify_single_flag_helper
{
static_assert((flag != 0) && ((flag & (flag - 1)) == 0), "Single flag expected, zero or multiple flags found");
static const unsigned long long value = flag;
};
}
/// @endcond
//! @defgroup typesafety Type Validation
//! Helpers to validate variable types to prevent accidental, but allowed type conversions.
//! These helpers are most useful when building macros that accept a particular type. Putting these functions around the types accepted
//! prior to pushing that type through to a function (or using it within the macro) allows the macro to add an additional layer of type
//! safety that would ordinarily be stripped away by C++ implicit conversions. This system is extensively used in the error handling helper
//! macros to validate the types given to various macro parameters.
//! @{
/** Verify that `val` can be evaluated as a logical bool.
Other types will generate an intentional compilation error. Allowed types for a logical bool are bool, BOOL,
boolean, BOOLEAN, and classes with an explicit bool cast.
@param val The logical bool expression
@return A C++ bool representing the evaluation of `val`. */
template <typename T, __R_ENABLE_IF_IS_CLASS(T)>
_Post_satisfies_(return == static_cast<bool>(val))
__forceinline constexpr bool verify_bool(const T& val)
{
return static_cast<bool>(val);
}
template <typename T, __R_ENABLE_IF_IS_NOT_CLASS(T)>
__forceinline constexpr bool verify_bool(T /*val*/)
{
static_assert(!wistd::is_same<T, T>::value, "Wrong Type: bool/BOOL/BOOLEAN/boolean expected");
return false;
}
template <>
_Post_satisfies_(return == val)
__forceinline constexpr bool verify_bool<bool>(bool val)
{
return val;
}
template <>
_Post_satisfies_(return == (val != 0))
__forceinline constexpr bool verify_bool<int>(int val)
{
return (val != 0);
}
template <>
_Post_satisfies_(return == !!val)
__forceinline constexpr bool verify_bool<unsigned char>(unsigned char val)
{
return !!val;
}
/** Verify that `val` is a Win32 BOOL value.
Other types (including other logical bool expressions) will generate an intentional compilation error. Note that this will
accept any `int` value as long as that is the underlying typedef behind `BOOL`.
@param val The Win32 BOOL returning expression
@return A Win32 BOOL representing the evaluation of `val`. */
template <typename T>
_Post_satisfies_(return == val)
__forceinline constexpr int verify_BOOL(T val)
{
// Note: Written in terms of 'int' as BOOL is actually: typedef int BOOL;
static_assert((wistd::is_same<T, int>::value), "Wrong Type: BOOL expected");
return val;
}
/** Verify that `hr` is an HRESULT value.
Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is the
underlying typedef behind HRESULT.
//!
Note that occasionally you might run into an HRESULT which is directly defined with a #define, such as:
~~~~
#define UIA_E_NOTSUPPORTED 0x80040204
~~~~
Though this looks like an `HRESULT`, this is actually an `unsigned long` (the hex specification forces this). When
these are encountered and they are NOT in the public SDK (have not yet shipped to the public), then you should change
their definition to match the manner in which `HRESULT` constants are defined in winerror.h:
~~~~
#define E_NOTIMPL _HRESULT_TYPEDEF_(0x80004001L)
~~~~
When these are encountered in the public SDK, their type should not be changed and you should use a static_cast
to use this value in a macro that utilizes `verify_hresult`, for example:
~~~~
RETURN_HR_IF(static_cast<HRESULT>(UIA_E_NOTSUPPORTED), (patternId != UIA_DragPatternId));
~~~~
@param val The HRESULT returning expression
@return An HRESULT representing the evaluation of `val`. */
template <typename T>
_Post_satisfies_(return == hr)
inline constexpr long verify_hresult(T hr)
{
// Note: Written in terms of 'int' as HRESULT is actually: typedef _Return_type_success_(return >= 0) long HRESULT
static_assert(wistd::is_same<T, long>::value, "Wrong Type: HRESULT expected");
return hr;
}
/// @} // end type validation routines
/// @cond
// Implementation details for macros and helper functions... do not use directly.
namespace details
{
// Use size-specific casts to avoid sign extending numbers -- avoid warning C4310: cast truncates constant value
#define __WI_MAKE_UNSIGNED(val) \
(__pragma(warning(push)) __pragma(warning(disable: 4310 4309)) (sizeof(val) == 1 ? static_cast<unsigned char>(val) : \
sizeof(val) == 2 ? static_cast<unsigned short>(val) : \
sizeof(val) == 4 ? static_cast<unsigned long>(val) : \
static_cast<unsigned long long>(val)) __pragma(warning(pop)))
#define __WI_IS_UNSIGNED_SINGLE_FLAG_SET(val) ((val) && !((val) & ((val) - 1)))
#define __WI_IS_SINGLE_FLAG_SET(val) __WI_IS_UNSIGNED_SINGLE_FLAG_SET(__WI_MAKE_UNSIGNED(val))
template <typename TVal, typename TFlags>
__forceinline constexpr bool AreAllFlagsSetHelper(TVal val, TFlags flags)
{
return ((val & flags) == static_cast<decltype(val & flags)>(flags));
}
template <typename TVal>
__forceinline constexpr bool IsSingleFlagSetHelper(TVal val)
{
return __WI_IS_SINGLE_FLAG_SET(val);
}
template <typename TVal>
__forceinline constexpr bool IsClearOrSingleFlagSetHelper(TVal val)
{
return ((val == static_cast<wistd::remove_reference_t<TVal>>(0)) || IsSingleFlagSetHelper(val));
}
template <typename TVal, typename TMask, typename TFlags>
__forceinline constexpr void UpdateFlagsInMaskHelper(_Inout_ TVal& val, TMask mask, TFlags flags)
{
val = static_cast<wistd::remove_reference_t<TVal>>((val & ~mask) | (flags & mask));
}
template <long>
struct variable_size;
template <>
struct variable_size<1>
{
typedef unsigned char type;
};
template <>
struct variable_size<2>
{
typedef unsigned short type;
};
template <>
struct variable_size<4>
{
typedef unsigned long type;
};
template <>
struct variable_size<8>
{
typedef unsigned long long type;
};
template <typename T>
struct variable_size_mapping
{
typedef typename variable_size<sizeof(T)>::type type;
};
} // details
/// @endcond
/** Defines the unsigned type of the same width (1, 2, 4, or 8 bytes) as the given type.
This allows code to generically convert any enum class to it's corresponding underlying type. */
template <typename T>
using integral_from_enum = typename details::variable_size_mapping<T>::type;
} // wil
#pragma warning(pop)
#endif // __cplusplus
#endif // __WIL_COMMON_INCLUDED

251
Externals/WIL/include/wil/cppwinrt.h vendored Normal file
View file

@ -0,0 +1,251 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_CPPWINRT_INCLUDED
#define __WIL_CPPWINRT_INCLUDED
#include "common.h"
#include <windows.h>
#include <unknwn.h>
#include <hstring.h>
// WIL and C++/WinRT use two different exception types for communicating HRESULT failures. Thus, both libraries need to
// understand how to translate these exception types into the correct HRESULT values at the ABI boundary. Prior to
// C++/WinRT "2.0" this was accomplished by injecting the WINRT_EXTERNAL_CATCH_CLAUSE macro - that WIL defines below -
// into its exception handler (winrt::to_hresult). Starting with C++/WinRT "2.0" this mechanism has shifted to a global
// function pointer - winrt_to_hresult_handler - that WIL sets automatically when this header is included and
// 'CPPWINRT_SUPPRESS_STATIC_INITIALIZERS' is not defined.
/// @cond
namespace wil::details
{
// Since the C++/WinRT version macro is a string...
inline constexpr int major_version_from_string(const char* versionString)
{
int result = 0;
auto str = versionString;
while ((*str >= '0') && (*str <= '9'))
{
result = result * 10 + (*str - '0');
++str;
}
return result;
}
}
/// @endcond
#ifdef CPPWINRT_VERSION
// Prior to C++/WinRT "2.0" this header needed to be included before 'winrt/base.h' so that our definition of
// 'WINRT_EXTERNAL_CATCH_CLAUSE' would get picked up in the implementation of 'winrt::to_hresult'. This is no longer
// problematic, so only emit an error when using a version of C++/WinRT prior to 2.0
static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2,
"Please include wil/cppwinrt.h before including any C++/WinRT headers");
#endif
// NOTE: Will eventually be removed once C++/WinRT 2.0 use can be assumed
#ifdef WINRT_EXTERNAL_CATCH_CLAUSE
#define __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE 1
#else
#define WINRT_EXTERNAL_CATCH_CLAUSE \
catch (const wil::ResultException& e) \
{ \
return winrt::hresult_error(e.GetErrorCode(), winrt::to_hstring(e.what())).to_abi(); \
}
#endif
#include "result_macros.h"
#include <winrt/base.h>
#if __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE
static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2,
"C++/WinRT external catch clause already defined outside of WIL");
#endif
// In C++/WinRT 2.0 and beyond, this function pointer exists. In earlier versions it does not. It's much easier to avoid
// linker errors than it is to SFINAE on variable existence, so we declare the variable here, but are careful not to
// use it unless the version of C++/WinRT is high enough
extern std::int32_t(__stdcall* winrt_to_hresult_handler)(void*) noexcept;
/// @cond
namespace wil::details
{
inline void MaybeGetExceptionString(
const winrt::hresult_error& exception,
_Out_writes_opt_(debugStringChars) PWSTR debugString,
_When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars)
{
if (debugString)
{
StringCchPrintfW(debugString, debugStringChars, L"winrt::hresult_error: %ls", exception.message().c_str());
}
}
inline HRESULT __stdcall ResultFromCaughtException_CppWinRt(
_Inout_updates_opt_(debugStringChars) PWSTR debugString,
_When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars,
_Inout_ bool* isNormalized) noexcept
{
if (g_pfnResultFromCaughtException)
{
try
{
throw;
}
catch (const ResultException& exception)
{
*isNormalized = true;
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.GetErrorCode();
}
catch (const winrt::hresult_error& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.code().value;
}
catch (const std::bad_alloc& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_OUTOFMEMORY;
}
catch (const std::out_of_range& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_BOUNDS;
}
catch (const std::invalid_argument& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_INVALIDARG;
}
catch (...)
{
auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars);
if (FAILED(hr))
{
return hr;
}
}
}
else
{
try
{
throw;
}
catch (const ResultException& exception)
{
*isNormalized = true;
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.GetErrorCode();
}
catch (const winrt::hresult_error& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.code().value;
}
catch (const std::bad_alloc& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_OUTOFMEMORY;
}
catch (const std::out_of_range& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_BOUNDS;
}
catch (const std::invalid_argument& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_INVALIDARG;
}
catch (const std::exception& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION);
}
catch (...)
{
// Fall through to returning 'S_OK' below
}
}
// Tell the caller that we were unable to map the exception by succeeding...
return S_OK;
}
}
/// @endcond
namespace wil
{
inline std::int32_t __stdcall winrt_to_hresult(void* returnAddress) noexcept
{
// C++/WinRT only gives us the return address (caller), so pass along an empty 'DiagnosticsInfo' since we don't
// have accurate file/line/etc. information
return static_cast<std::int32_t>(details::ReportFailure_CaughtException<FailureType::Return>(__R_DIAGNOSTICS_RA(DiagnosticsInfo{}, returnAddress)));
}
inline void WilInitialize_CppWinRT()
{
details::g_pfnResultFromCaughtException_CppWinRt = details::ResultFromCaughtException_CppWinRt;
if constexpr (details::major_version_from_string(CPPWINRT_VERSION) >= 2)
{
WI_ASSERT(winrt_to_hresult_handler == nullptr);
winrt_to_hresult_handler = winrt_to_hresult;
}
}
/// @cond
namespace details
{
#ifndef CPPWINRT_SUPPRESS_STATIC_INITIALIZERS
WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "0")
WI_HEADER_INITITALIZATION_FUNCTION(WilInitialize_CppWinRT, []
{
::wil::WilInitialize_CppWinRT();
return 1;
});
#else
WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "1")
#endif
}
/// @endcond
// Provides an overload of verify_hresult so that the WIL macros can recognize winrt::hresult as a valid "hresult" type.
inline long verify_hresult(winrt::hresult hr) noexcept
{
return hr;
}
// Provides versions of get_abi and put_abi for genericity that directly use HSTRING for convenience.
template <typename T>
auto get_abi(T const& object) noexcept
{
return winrt::get_abi(object);
}
inline auto get_abi(winrt::hstring const& object) noexcept
{
return static_cast<HSTRING>(winrt::get_abi(object));
}
template <typename T>
auto put_abi(T& object) noexcept
{
return winrt::put_abi(object);
}
inline auto put_abi(winrt::hstring& object) noexcept
{
return reinterpret_cast<HSTRING*>(winrt::put_abi(object));
}
}
#endif // __WIL_CPPWINRT_INCLUDED

908
Externals/WIL/include/wil/filesystem.h vendored Normal file
View file

@ -0,0 +1,908 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_FILESYSTEM_INCLUDED
#define __WIL_FILESYSTEM_INCLUDED
#ifdef _KERNEL_MODE
#error This header is not supported in kernel-mode.
#endif
#include <new>
#include <combaseapi.h> // Needed for CoTaskMemFree() used in output of some helpers.
#include <winbase.h> // LocalAlloc
#include <PathCch.h>
#include "result.h"
#include "win32_helpers.h"
#include "resource.h"
namespace wil
{
//! Determines if a path is an extended length path that can be used to access paths longer than MAX_PATH.
inline bool is_extended_length_path(_In_ PCWSTR path)
{
return wcsncmp(path, L"\\\\?\\", 4) == 0;
}
//! Find the last segment of a path. Matches the behavior of shlwapi!PathFindFileNameW()
//! note, does not support streams being specified like PathFindFileNameW(), is that a bug or a feature?
inline PCWSTR find_last_path_segment(_In_ PCWSTR path)
{
auto const pathLength = wcslen(path);
// If there is a trailing slash ignore that in the search.
auto const limitedLength = ((pathLength > 0) && (path[pathLength - 1] == L'\\')) ? (pathLength - 1) : pathLength;
PCWSTR result;
auto const offset = FindStringOrdinal(FIND_FROMEND, path, static_cast<int>(limitedLength), L"\\", 1, TRUE);
if (offset == -1)
{
result = path + pathLength; // null terminator
}
else
{
result = path + offset + 1; // just past the slash
}
return result;
}
//! Determine if the file name is one of the special "." or ".." names.
inline bool path_is_dot_or_dotdot(_In_ PCWSTR fileName)
{
return ((fileName[0] == L'.') &&
((fileName[1] == L'\0') || ((fileName[1] == L'.') && (fileName[2] == L'\0'))));
}
//! Returns the drive number, if it has one. Returns true if there is a drive number, false otherwise. Supports regular and extended length paths.
inline bool try_get_drive_letter_number(_In_ PCWSTR path, _Out_ int* driveNumber)
{
if (path[0] == L'\\' && path[1] == L'\\' && path[2] == L'?' && path[3] == L'\\')
{
path += 4;
}
if (path[0] && (path[1] == L':'))
{
if ((path[0] >= L'a') && (path[0] <= L'z'))
{
*driveNumber = path[0] - L'a';
return true;
}
else if ((path[0] >= L'A') && (path[0] <= L'Z'))
{
*driveNumber = path[0] - L'A';
return true;
}
}
*driveNumber = -1;
return false;
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
// PathCch.h APIs are only in desktop API for now.
// Compute the substring in the input value that is the parent folder path.
// returns:
// true + parentPathLength - path has a parent starting at the beginning path and of parentPathLength length.
// false, no parent path, the input is a root path.
inline bool try_get_parent_path_range(_In_ PCWSTR path, _Out_ size_t* parentPathLength)
{
*parentPathLength = 0;
bool hasParent = false;
PCWSTR rootEnd;
if (SUCCEEDED(PathCchSkipRoot(path, &rootEnd)) && (*rootEnd != L'\0'))
{
auto const lastSegment = find_last_path_segment(path);
*parentPathLength = lastSegment - path;
hasParent = (*parentPathLength != 0);
}
return hasParent;
}
// Creates directories for the specified path, creating parent paths
// as needed.
inline HRESULT CreateDirectoryDeepNoThrow(PCWSTR path) WI_NOEXCEPT
{
if (::CreateDirectoryW(path, nullptr) == FALSE)
{
DWORD const lastError = ::GetLastError();
if (lastError == ERROR_PATH_NOT_FOUND)
{
size_t parentLength;
if (try_get_parent_path_range(path, &parentLength))
{
wistd::unique_ptr<wchar_t[]> parent(new (std::nothrow) wchar_t[parentLength + 1]);
RETURN_IF_NULL_ALLOC(parent.get());
RETURN_IF_FAILED(StringCchCopyNW(parent.get(), parentLength + 1, path, parentLength));
CreateDirectoryDeepNoThrow(parent.get()); // recurs
}
RETURN_IF_WIN32_BOOL_FALSE(::CreateDirectoryW(path, nullptr));
}
else if (lastError != ERROR_ALREADY_EXISTS)
{
RETURN_WIN32(lastError);
}
}
return S_OK;
}
#ifdef WIL_ENABLE_EXCEPTIONS
inline void CreateDirectoryDeep(PCWSTR path)
{
THROW_IF_FAILED(CreateDirectoryDeepNoThrow(path));
}
#endif // WIL_ENABLE_EXCEPTIONS
//! A strongly typed version of the Win32 API GetFullPathNameW.
//! Return a path in an allocated buffer for handling long paths.
//! Optionally return the pointer to the file name part.
template <typename string_type, size_t stackBufferLength = 256>
HRESULT GetFullPathNameW(PCWSTR file, string_type& path, _Outptr_opt_ PCWSTR* filePart = nullptr)
{
wil::assign_null_to_opt_param(filePart);
const auto hr = AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(path,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT
{
// Note that GetFullPathNameW() is not limited to MAX_PATH
// but it does take a fixed size buffer.
*valueLengthNeededWithNull = ::GetFullPathNameW(file, static_cast<DWORD>(valueLength), value, nullptr);
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0);
WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength));
if (*valueLengthNeededWithNull < valueLength)
{
(*valueLengthNeededWithNull)++; // it fit, account for the null
}
return S_OK;
});
if (SUCCEEDED(hr) && filePart)
{
*filePart = wil::find_last_path_segment(details::string_maker<string_type>::get(path));
}
return hr;
}
#ifdef WIL_ENABLE_EXCEPTIONS
//! A strongly typed version of the Win32 API of GetFullPathNameW.
//! Return a path in an allocated buffer for handling long paths.
//! Optionally return the pointer to the file name part.
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
string_type GetFullPathNameW(PCWSTR file, _Outptr_opt_ PCWSTR* filePart = nullptr)
{
string_type result;
THROW_IF_FAILED((GetFullPathNameW<string_type, stackBufferLength>(file, result, filePart)));
return result;
}
#endif
enum class RemoveDirectoryOptions
{
None = 0,
KeepRootDirectory = 0x1
};
DEFINE_ENUM_FLAG_OPERATORS(RemoveDirectoryOptions);
// If inputPath is a non-normalized name be sure to pass an extended length form to ensure
// it can be addressed and deleted.
inline HRESULT RemoveDirectoryRecursiveNoThrow(PCWSTR inputPath, RemoveDirectoryOptions options = RemoveDirectoryOptions::None) WI_NOEXCEPT
{
wil::unique_hlocal_string path;
PATHCCH_OPTIONS combineOptions = PATHCCH_NONE;
if (is_extended_length_path(inputPath))
{
path = wil::make_hlocal_string_nothrow(inputPath);
RETURN_IF_NULL_ALLOC(path);
// PathAllocCombine will convert extended length paths to regular paths if shorter than
// MAX_PATH, avoid that behavior to provide access inputPath with non-normalized names.
combineOptions = PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH;
}
else
{
// For regular paths normalize here to get consistent results when searching and deleting.
RETURN_IF_FAILED(wil::GetFullPathNameW(inputPath, path));
combineOptions = PATHCCH_ALLOW_LONG_PATHS;
}
wil::unique_hlocal_string searchPath;
RETURN_IF_FAILED(::PathAllocCombine(path.get(), L"*", combineOptions, &searchPath));
WIN32_FIND_DATAW fd;
wil::unique_hfind findHandle(::FindFirstFileW(searchPath.get(), &fd));
RETURN_LAST_ERROR_IF(!findHandle);
for (;;)
{
// skip "." and ".."
if (!(WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY) && path_is_dot_or_dotdot(fd.cFileName)))
{
// Need to form an extended length path to provide the ability to delete paths > MAX_PATH
// and files with non-normalized names (dots or spaces at the end).
wil::unique_hlocal_string pathToDelete;
RETURN_IF_FAILED(::PathAllocCombine(path.get(), fd.cFileName,
PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH | PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, &pathToDelete));
if (WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY))
{
RemoveDirectoryOptions localOptions = options;
RETURN_IF_FAILED(RemoveDirectoryRecursiveNoThrow(pathToDelete.get(), WI_ClearFlag(localOptions, RemoveDirectoryOptions::KeepRootDirectory)));
}
else
{
// note: if pathToDelete is read-only this will fail, consider adding
// RemoveDirectoryOptions::RemoveReadOnly to enable this behavior.
RETURN_IF_WIN32_BOOL_FALSE(::DeleteFileW(pathToDelete.get()));
}
}
if (!::FindNextFileW(findHandle.get(), &fd))
{
auto const err = ::GetLastError();
if (err == ERROR_NO_MORE_FILES)
{
break;
}
RETURN_WIN32(err);
}
}
if (WI_IsFlagClear(options, RemoveDirectoryOptions::KeepRootDirectory))
{
RETURN_IF_WIN32_BOOL_FALSE(::RemoveDirectoryW(path.get()));
}
return S_OK;
}
#ifdef WIL_ENABLE_EXCEPTIONS
inline void RemoveDirectoryRecursive(PCWSTR path, RemoveDirectoryOptions options = RemoveDirectoryOptions::None)
{
THROW_IF_FAILED(RemoveDirectoryRecursiveNoThrow(path, options));
}
#endif // WIL_ENABLE_EXCEPTIONS
// Range based for that supports Win32 structures that use NextEntryOffset as the basis of traversing
// a result buffer that contains data. This is used in the following FileIO calls:
// FileStreamInfo, FILE_STREAM_INFO
// FileIdBothDirectoryInfo, FILE_ID_BOTH_DIR_INFO
// FileFullDirectoryInfo, FILE_FULL_DIR_INFO
// FileIdExtdDirectoryInfo, FILE_ID_EXTD_DIR_INFO
// ReadDirectoryChangesW, FILE_NOTIFY_INFORMATION
template <typename T>
struct next_entry_offset_iterator
{
// Fulfill std::iterator_traits requirements
using difference_type = ptrdiff_t;
using value_type = T;
using pointer = const T*;
using reference = const T&;
#ifdef _XUTILITY_
using iterator_category = ::std::forward_iterator_tag;
#endif
next_entry_offset_iterator(T *iterable = __nullptr) : current_(iterable) {}
// range based for requires operator!=, operator++ and operator* to do its work
// on the type returned from begin() and end(), provide those here.
bool operator!=(const next_entry_offset_iterator& other) const { return current_ != other.current_; }
next_entry_offset_iterator& operator++()
{
current_ = (current_->NextEntryOffset != 0) ?
reinterpret_cast<T *>(reinterpret_cast<unsigned char*>(current_) + current_->NextEntryOffset) :
__nullptr;
return *this;
}
next_entry_offset_iterator operator++(int)
{
auto copy = *this;
++(*this);
return copy;
}
reference operator*() const WI_NOEXCEPT { return *current_; }
pointer operator->() const WI_NOEXCEPT { return current_; }
next_entry_offset_iterator<T> begin() { return *this; }
next_entry_offset_iterator<T> end() { return next_entry_offset_iterator<T>(); }
T* current_;
};
template <typename T>
next_entry_offset_iterator<T> create_next_entry_offset_iterator(T* p)
{
return next_entry_offset_iterator<T>(p);
}
#pragma region Folder Watcher
// Example use in exception based code:
// auto watcher = wil::make_folder_watcher(folder.Path().c_str(), true, wil::allChangeEvents, []()
// {
// // respond
// });
//
// Example use in result code based code:
// wil::unique_folder_watcher watcher;
// THROW_IF_FAILED(watcher.create(folder, true, wil::allChangeEvents, []()
// {
// // respond
// }));
enum class FolderChangeEvent : DWORD
{
ChangesLost = 0, // requies special handling, reset state as events were lost
Added = FILE_ACTION_ADDED,
Removed = FILE_ACTION_REMOVED,
Modified = FILE_ACTION_MODIFIED,
RenameOldName = FILE_ACTION_RENAMED_OLD_NAME,
RenameNewName = FILE_ACTION_RENAMED_NEW_NAME,
};
enum class FolderChangeEvents : DWORD
{
None = 0,
FileName = FILE_NOTIFY_CHANGE_FILE_NAME,
DirectoryName = FILE_NOTIFY_CHANGE_DIR_NAME,
Attributes = FILE_NOTIFY_CHANGE_ATTRIBUTES,
FileSize = FILE_NOTIFY_CHANGE_SIZE,
LastWriteTime = FILE_NOTIFY_CHANGE_LAST_WRITE,
Security = FILE_NOTIFY_CHANGE_SECURITY,
All = FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME |
FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_SIZE |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_SECURITY
};
DEFINE_ENUM_FLAG_OPERATORS(FolderChangeEvents);
/// @cond
namespace details
{
struct folder_watcher_state
{
folder_watcher_state(wistd::function<void()> &&callback) : m_callback(wistd::move(callback))
{
}
wistd::function<void()> m_callback;
// Order is important, need to close the thread pool wait before the change handle.
unique_hfind_change m_findChangeHandle;
unique_threadpool_wait m_threadPoolWait;
};
inline void delete_folder_watcher_state(_In_opt_ folder_watcher_state *storage) { delete storage; }
typedef resource_policy<folder_watcher_state *, decltype(&details::delete_folder_watcher_state),
details::delete_folder_watcher_state, details::pointer_access_none> folder_watcher_state_resource_policy;
}
/// @endcond
template <typename storage_t, typename err_policy = err_exception_policy>
class folder_watcher_t : public storage_t
{
public:
// forward all base class constructors...
template <typename... args_t>
explicit folder_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward<args_t>(args)...) {}
// HRESULT or void error handling...
typedef typename err_policy::result result;
// Exception-based constructors
folder_watcher_t(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback)
{
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
create(folderToWatch, isRecursive, filter, wistd::move(callback));
}
result create(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback)
{
return err_policy::HResult(create_common(folderToWatch, isRecursive, filter, wistd::move(callback)));
}
private:
// Factored into a standalone function to support Clang which does not support conversion of stateless lambdas
// to __stdcall
static void __stdcall callback(PTP_CALLBACK_INSTANCE /*Instance*/, void *context, TP_WAIT *pThreadPoolWait, TP_WAIT_RESULT /*result*/)
{
auto watcherState = static_cast<details::folder_watcher_state *>(context);
watcherState->m_callback();
// Rearm the wait. Should not fail with valid parameters.
FindNextChangeNotification(watcherState->m_findChangeHandle.get());
SetThreadpoolWait(pThreadPoolWait, watcherState->m_findChangeHandle.get(), __nullptr);
}
// This function exists to avoid template expansion of this code based on err_policy.
HRESULT create_common(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback)
{
wistd::unique_ptr<details::folder_watcher_state> watcherState(new(std::nothrow) details::folder_watcher_state(wistd::move(callback)));
RETURN_IF_NULL_ALLOC(watcherState);
watcherState->m_findChangeHandle.reset(FindFirstChangeNotificationW(folderToWatch, isRecursive, static_cast<DWORD>(filter)));
RETURN_LAST_ERROR_IF(!watcherState->m_findChangeHandle);
watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(&folder_watcher_t::callback, watcherState.get(), __nullptr));
RETURN_LAST_ERROR_IF(!watcherState->m_threadPoolWait);
this->reset(watcherState.release()); // no more failures after this, pass ownership
SetThreadpoolWait(this->get()->m_threadPoolWait.get(), this->get()->m_findChangeHandle.get(), __nullptr);
return S_OK;
}
};
typedef unique_any_t<folder_watcher_t<details::unique_storage<details::folder_watcher_state_resource_policy>, err_returncode_policy>> unique_folder_watcher_nothrow;
inline unique_folder_watcher_nothrow make_folder_watcher_nothrow(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback) WI_NOEXCEPT
{
unique_folder_watcher_nothrow watcher;
watcher.create(folderToWatch, isRecursive, filter, wistd::move(callback));
return watcher; // caller must test for success using if (watcher)
}
#ifdef WIL_ENABLE_EXCEPTIONS
typedef unique_any_t<folder_watcher_t<details::unique_storage<details::folder_watcher_state_resource_policy>, err_exception_policy>> unique_folder_watcher;
inline unique_folder_watcher make_folder_watcher(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback)
{
return unique_folder_watcher(folderToWatch, isRecursive, filter, wistd::move(callback));
}
#endif // WIL_ENABLE_EXCEPTIONS
#pragma endregion
#pragma region Folder Reader
// Example use for throwing:
// auto reader = wil::make_folder_change_reader(folder.Path().c_str(), true, wil::FolderChangeEvents::All,
// [](wil::FolderChangeEvent event, PCWSTR fileName)
// {
// switch (event)
// {
// case wil::FolderChangeEvent::ChangesLost: break;
// case wil::FolderChangeEvent::Added: break;
// case wil::FolderChangeEvent::Removed: break;
// case wil::FolderChangeEvent::Modified: break;
// case wil::FolderChangeEvent::RenamedOldName: break;
// case wil::FolderChangeEvent::RenamedNewName: break;
// });
//
// Example use for non throwing:
// wil::unique_folder_change_reader_nothrow reader;
// THROW_IF_FAILED(reader.create(folder, true, wil::FolderChangeEvents::All,
// [](wil::FolderChangeEvent event, PCWSTR fileName)
// {
// // handle changes
// }));
//
// @cond
namespace details
{
struct folder_change_reader_state
{
folder_change_reader_state(bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
: m_callback(wistd::move(callback)), m_isRecursive(isRecursive), m_filter(filter)
{
}
~folder_change_reader_state()
{
if (m_tpIo != __nullptr)
{
TP_IO *tpIo = m_tpIo;
// Indicate to the callback function that this object is being torn
// down.
{
auto autoLock = m_cancelLock.lock_exclusive();
m_tpIo = __nullptr;
}
// Cancel IO to terminate the file system monitoring operation.
if (m_folderHandle)
{
CancelIoEx(m_folderHandle.get(), &m_overlapped);
}
// Wait for callbacks to complete.
//
// N.B. This is a blocking call and must not be made within a
// callback or within a lock which is taken inside the
// callback.
WaitForThreadpoolIoCallbacks(tpIo, TRUE);
CloseThreadpoolIo(tpIo);
}
}
HRESULT StartIo()
{
// Unfortunately we have to handle ref-counting of IOs on behalf of the
// thread pool.
StartThreadpoolIo(m_tpIo);
HRESULT hr = ReadDirectoryChangesW(m_folderHandle.get(), m_readBuffer, sizeof(m_readBuffer),
m_isRecursive, static_cast<DWORD>(m_filter), __nullptr, &m_overlapped, __nullptr) ?
S_OK : HRESULT_FROM_WIN32(::GetLastError());
if (FAILED(hr))
{
// This operation does not have the usual semantic of returning
// ERROR_IO_PENDING.
// WI_ASSERT(hr != HRESULT_FROM_WIN32(ERROR_IO_PENDING));
// If the operation failed for whatever reason, ensure the TP
// ref counts are accurate.
CancelThreadpoolIo(m_tpIo);
}
return hr;
}
// void (wil::FolderChangeEvent event, PCWSTR fileName)
wistd::function<void(FolderChangeEvent, PCWSTR)> m_callback;
unique_handle m_folderHandle;
BOOL m_isRecursive = FALSE;
FolderChangeEvents m_filter = FolderChangeEvents::None;
OVERLAPPED m_overlapped{};
TP_IO *m_tpIo = __nullptr;
srwlock m_cancelLock;
char m_readBuffer[4096]; // Consider alternative buffer sizes. With 512 byte buffer i was not able to observe overflow.
};
inline void delete_folder_change_reader_state(_In_opt_ folder_change_reader_state *storage) { delete storage; }
typedef resource_policy<folder_change_reader_state *, decltype(&details::delete_folder_change_reader_state),
details::delete_folder_change_reader_state, details::pointer_access_none> folder_change_reader_state_resource_policy;
}
/// @endcond
template <typename storage_t, typename err_policy = err_exception_policy>
class folder_change_reader_t : public storage_t
{
public:
// forward all base class constructors...
template <typename... args_t>
explicit folder_change_reader_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward<args_t>(args)...) {}
// HRESULT or void error handling...
typedef typename err_policy::result result;
// Exception-based constructors
folder_change_reader_t(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
{
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
create(folderToWatch, isRecursive, filter, wistd::move(callback));
}
result create(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
{
return err_policy::HResult(create_common(folderToWatch, isRecursive, filter, wistd::move(callback)));
}
wil::unique_hfile& folder_handle() { return this->get()->m_folderHandle; }
private:
// Factored into a standalone function to support Clang which does not support conversion of stateless lambdas
// to __stdcall
static void __stdcall callback(PTP_CALLBACK_INSTANCE /* Instance */, void *context, void * /*overlapped*/,
ULONG result, ULONG_PTR /* BytesTransferred */, TP_IO * /* Io */)
{
auto readerState = static_cast<details::folder_change_reader_state *>(context);
// WI_ASSERT(overlapped == &readerState->m_overlapped);
bool requeue = true;
if (result == ERROR_SUCCESS)
{
for (auto const& info : create_next_entry_offset_iterator(reinterpret_cast<FILE_NOTIFY_INFORMATION *>(readerState->m_readBuffer)))
{
wchar_t realtiveFileName[MAX_PATH];
StringCchCopyNW(realtiveFileName, ARRAYSIZE(realtiveFileName), info.FileName, info.FileNameLength / sizeof(info.FileName[0]));
readerState->m_callback(static_cast<FolderChangeEvent>(info.Action), realtiveFileName);
}
}
else if (result == ERROR_NOTIFY_ENUM_DIR)
{
readerState->m_callback(FolderChangeEvent::ChangesLost, __nullptr);
}
else
{
requeue = false;
}
if (requeue)
{
// If the lock is held non-shared or the TP IO is nullptr, this
// structure is being torn down. Otherwise, monitor for further
// changes.
auto autoLock = readerState->m_cancelLock.try_lock_shared();
if (autoLock && readerState->m_tpIo)
{
readerState->StartIo(); // ignoring failure here
}
}
}
// This function exists to avoid template expansion of this code based on err_policy.
HRESULT create_common(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
{
wistd::unique_ptr<details::folder_change_reader_state> readerState(new(std::nothrow) details::folder_change_reader_state(
isRecursive, filter, wistd::move(callback)));
RETURN_IF_NULL_ALLOC(readerState);
readerState->m_folderHandle.reset(CreateFileW(folderToWatch,
FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
__nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, __nullptr));
RETURN_LAST_ERROR_IF(!readerState->m_folderHandle);
readerState->m_tpIo = CreateThreadpoolIo(readerState->m_folderHandle.get(), &folder_change_reader_t::callback, readerState.get(), __nullptr);
RETURN_LAST_ERROR_IF_NULL(readerState->m_tpIo);
RETURN_IF_FAILED(readerState->StartIo());
this->reset(readerState.release());
return S_OK;
}
};
typedef unique_any_t<folder_change_reader_t<details::unique_storage<details::folder_change_reader_state_resource_policy>, err_returncode_policy>> unique_folder_change_reader_nothrow;
inline unique_folder_change_reader_nothrow make_folder_change_reader_nothrow(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter,
wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback) WI_NOEXCEPT
{
unique_folder_change_reader_nothrow watcher;
watcher.create(folderToWatch, isRecursive, filter, wistd::move(callback));
return watcher; // caller must test for success using if (watcher)
}
#ifdef WIL_ENABLE_EXCEPTIONS
typedef unique_any_t<folder_change_reader_t<details::unique_storage<details::folder_change_reader_state_resource_policy>, err_exception_policy>> unique_folder_change_reader;
inline unique_folder_change_reader make_folder_change_reader(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter,
wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
{
return unique_folder_change_reader(folderToWatch, isRecursive, filter, wistd::move(callback));
}
#endif // WIL_ENABLE_EXCEPTIONS
#pragma endregion
//! Dos and VolumeGuid paths are always extended length paths with the \\?\ prefix.
enum class VolumePrefix
{
Dos = VOLUME_NAME_DOS, // Extended Dos Device path form, e.g. \\?\C:\Users\Chris\AppData\Local\Temp\wil8C31.tmp
VolumeGuid = VOLUME_NAME_GUID, // \\?\Volume{588fb606-b95b-4eae-b3cb-1e49861aaf18}\Users\Chris\AppData\Local\Temp\wil8C31.tmp
// The following are special paths which can't be used with Win32 APIs, but are useful in other scenarios.
None = VOLUME_NAME_NONE, // Path without the volume root, e.g. \Users\Chris\AppData\Local\Temp\wil8C31.tmp
NtObjectName = VOLUME_NAME_NT, // Unique name used by Object Manager, e.g. \Device\HarddiskVolume4\Users\Chris\AppData\Local\Temp\wil8C31.tmp
};
enum class PathOptions
{
Normalized = FILE_NAME_NORMALIZED,
Opened = FILE_NAME_OPENED,
};
DEFINE_ENUM_FLAG_OPERATORS(PathOptions);
/** A strongly typed version of the Win32 API GetFinalPathNameByHandleW.
Get the full path name in different forms
Use this instead + VolumePrefix::None instead of GetFileInformationByHandleEx(FileNameInfo) to
get that path form. */
template <typename string_type, size_t stackBufferLength = 256>
HRESULT GetFinalPathNameByHandleW(HANDLE fileHandle, string_type& path,
wil::VolumePrefix volumePrefix = wil::VolumePrefix::Dos, wil::PathOptions options = wil::PathOptions::Normalized)
{
return AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(path,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT
{
*valueLengthNeededWithNull = ::GetFinalPathNameByHandleW(fileHandle, value, static_cast<DWORD>(valueLength),
static_cast<DWORD>(volumePrefix) | static_cast<DWORD>(options));
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0);
WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength));
if (*valueLengthNeededWithNull < valueLength)
{
(*valueLengthNeededWithNull)++; // it fit, account for the null
}
return S_OK;
});
}
#ifdef WIL_ENABLE_EXCEPTIONS
/** A strongly typed version of the Win32 API GetFinalPathNameByHandleW.
Get the full path name in different forms. Use this + VolumePrefix::None
instead of GetFileInformationByHandleEx(FileNameInfo) to get that path form. */
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
string_type GetFinalPathNameByHandleW(HANDLE fileHandle,
wil::VolumePrefix volumePrefix = wil::VolumePrefix::Dos, wil::PathOptions options = wil::PathOptions::Normalized)
{
string_type result;
THROW_IF_FAILED((GetFinalPathNameByHandleW<string_type, stackBufferLength>(fileHandle, result, volumePrefix, options)));
return result;
}
#endif
//! A strongly typed version of the Win32 API of GetCurrentDirectoryW.
//! Return a path in an allocated buffer for handling long paths.
template <typename string_type, size_t stackBufferLength = 256>
HRESULT GetCurrentDirectoryW(string_type& path)
{
return AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(path,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT
{
*valueLengthNeededWithNull = ::GetCurrentDirectoryW(static_cast<DWORD>(valueLength), value);
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0);
WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength));
if (*valueLengthNeededWithNull < valueLength)
{
(*valueLengthNeededWithNull)++; // it fit, account for the null
}
return S_OK;
});
}
#ifdef WIL_ENABLE_EXCEPTIONS
//! A strongly typed version of the Win32 API of GetCurrentDirectoryW.
//! Return a path in an allocated buffer for handling long paths.
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
string_type GetCurrentDirectoryW()
{
string_type result;
THROW_IF_FAILED((GetCurrentDirectoryW<string_type, stackBufferLength>(result)));
return result;
}
#endif
// TODO: add support for these and other similar APIs.
// GetShortPathNameW()
// GetLongPathNameW()
// GetWindowsDirectory()
// GetTempDirectory()
/// @cond
namespace details
{
template <FILE_INFO_BY_HANDLE_CLASS infoClass> struct MapInfoClassToInfoStruct; // failure to map is a usage error caught by the compiler
#define MAP_INFOCLASS_TO_STRUCT(InfoClass, InfoStruct, IsFixed, Extra) \
template <> struct MapInfoClassToInfoStruct<InfoClass> \
{ \
typedef InfoStruct type; \
static bool const isFixed = IsFixed; \
static size_t const extraSize = Extra; \
};
MAP_INFOCLASS_TO_STRUCT(FileBasicInfo, FILE_BASIC_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileStandardInfo, FILE_STANDARD_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileNameInfo, FILE_NAME_INFO, false, 32);
MAP_INFOCLASS_TO_STRUCT(FileRenameInfo, FILE_RENAME_INFO, false, 32);
MAP_INFOCLASS_TO_STRUCT(FileDispositionInfo, FILE_DISPOSITION_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileAllocationInfo, FILE_ALLOCATION_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileEndOfFileInfo, FILE_END_OF_FILE_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileStreamInfo, FILE_STREAM_INFO, false, 32);
MAP_INFOCLASS_TO_STRUCT(FileCompressionInfo, FILE_COMPRESSION_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileAttributeTagInfo, FILE_ATTRIBUTE_TAG_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileIdBothDirectoryInfo, FILE_ID_BOTH_DIR_INFO, false, 4096);
MAP_INFOCLASS_TO_STRUCT(FileIdBothDirectoryRestartInfo, FILE_ID_BOTH_DIR_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileIoPriorityHintInfo, FILE_IO_PRIORITY_HINT_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileRemoteProtocolInfo, FILE_REMOTE_PROTOCOL_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileFullDirectoryInfo, FILE_FULL_DIR_INFO, false, 4096);
MAP_INFOCLASS_TO_STRUCT(FileFullDirectoryRestartInfo, FILE_FULL_DIR_INFO, true, 0);
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
MAP_INFOCLASS_TO_STRUCT(FileStorageInfo, FILE_STORAGE_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileAlignmentInfo, FILE_ALIGNMENT_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileIdInfo, FILE_ID_INFO, true, 0);
MAP_INFOCLASS_TO_STRUCT(FileIdExtdDirectoryInfo, FILE_ID_EXTD_DIR_INFO, false, 4096);
MAP_INFOCLASS_TO_STRUCT(FileIdExtdDirectoryRestartInfo, FILE_ID_EXTD_DIR_INFO, true, 0);
#endif
// Type unsafe version used in the implementation to avoid template bloat.
inline HRESULT GetFileInfo(HANDLE fileHandle, FILE_INFO_BY_HANDLE_CLASS infoClass, size_t allocationSize,
_Outptr_result_nullonfailure_ void **result)
{
*result = nullptr;
wistd::unique_ptr<char[]> resultHolder(new(std::nothrow) char[allocationSize]);
RETURN_IF_NULL_ALLOC(resultHolder);
for (;;)
{
if (GetFileInformationByHandleEx(fileHandle, infoClass, resultHolder.get(), static_cast<DWORD>(allocationSize)))
{
*result = resultHolder.release();
break;
}
else
{
DWORD const lastError = ::GetLastError();
if (lastError == ERROR_MORE_DATA)
{
allocationSize *= 2;
resultHolder.reset(new(std::nothrow) char[allocationSize]);
RETURN_IF_NULL_ALLOC(resultHolder);
}
else if (lastError == ERROR_NO_MORE_FILES) // for folder enumeration cases
{
break;
}
else if (lastError == ERROR_INVALID_PARAMETER) // operation not supported by file system
{
return HRESULT_FROM_WIN32(lastError);
}
else
{
RETURN_WIN32(lastError);
}
}
}
return S_OK;
}
}
/// @endcond
/** Get file information for a variable sized structure, returns an HRESULT.
~~~
wistd::unique_ptr<FILE_NAME_INFO> fileNameInfo;
RETURN_IF_FAILED(GetFileInfoNoThrow<FileNameInfo>(fileHandle, fileNameInfo));
~~~
*/
template <FILE_INFO_BY_HANDLE_CLASS infoClass, typename wistd::enable_if<!details::MapInfoClassToInfoStruct<infoClass>::isFixed, int>::type = 0>
HRESULT GetFileInfoNoThrow(HANDLE fileHandle, wistd::unique_ptr<typename details::MapInfoClassToInfoStruct<infoClass>::type> &result) WI_NOEXCEPT
{
void *rawResult;
HRESULT hr = details::GetFileInfo(fileHandle, infoClass,
sizeof(typename details::MapInfoClassToInfoStruct<infoClass>::type) + details::MapInfoClassToInfoStruct<infoClass>::extraSize,
&rawResult);
result.reset(static_cast<typename details::MapInfoClassToInfoStruct<infoClass>::type*>(rawResult));
RETURN_HR_IF_EXPECTED(hr, hr == E_INVALIDARG); // operation not supported by file system
RETURN_IF_FAILED(hr);
return S_OK;
}
/** Get file information for a fixed sized structure, returns an HRESULT.
~~~
FILE_BASIC_INFO fileBasicInfo;
RETURN_IF_FAILED(GetFileInfoNoThrow<FileBasicInfo>(fileHandle, &fileBasicInfo));
~~~
*/
template <FILE_INFO_BY_HANDLE_CLASS infoClass, typename wistd::enable_if<details::MapInfoClassToInfoStruct<infoClass>::isFixed, int>::type = 0>
HRESULT GetFileInfoNoThrow(HANDLE fileHandle, _Out_ typename details::MapInfoClassToInfoStruct<infoClass>::type *result) WI_NOEXCEPT
{
const HRESULT hr = GetFileInformationByHandleEx(fileHandle, infoClass, result, sizeof(*result)) ?
S_OK : HRESULT_FROM_WIN32(::GetLastError());
RETURN_HR_IF_EXPECTED(hr, hr == E_INVALIDARG); // operation not supported by file system
RETURN_IF_FAILED(hr);
return S_OK;
}
#ifdef _CPPUNWIND
/** Get file information for a fixed sized structure, throws on failure.
~~~
auto fileBasicInfo = GetFileInfo<FileBasicInfo>(fileHandle);
~~~
*/
template <FILE_INFO_BY_HANDLE_CLASS infoClass, typename wistd::enable_if<details::MapInfoClassToInfoStruct<infoClass>::isFixed, int>::type = 0>
typename details::MapInfoClassToInfoStruct<infoClass>::type GetFileInfo(HANDLE fileHandle)
{
typename details::MapInfoClassToInfoStruct<infoClass>::type result;
THROW_IF_FAILED(GetFileInfoNoThrow<infoClass>(fileHandle, &result));
return result;
}
/** Get file information for a variable sized structure, throws on failure.
~~~
auto fileBasicInfo = GetFileInfo<FileNameInfo>(fileHandle);
~~~
*/
template <FILE_INFO_BY_HANDLE_CLASS infoClass, typename wistd::enable_if<!details::MapInfoClassToInfoStruct<infoClass>::isFixed, int>::type = 0>
wistd::unique_ptr<typename details::MapInfoClassToInfoStruct<infoClass>::type> GetFileInfo(HANDLE fileHandle)
{
wistd::unique_ptr<typename details::MapInfoClassToInfoStruct<infoClass>::type> result;
THROW_IF_FAILED(GetFileInfoNoThrow<infoClass>(fileHandle, result));
return result;
}
#endif // _CPPUNWIND
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
}
#endif // __WIL_FILESYSTEM_INCLUDED

277
Externals/WIL/include/wil/registry.h vendored Normal file
View file

@ -0,0 +1,277 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_REGISTRY_INCLUDED
#define __WIL_REGISTRY_INCLUDED
#ifdef _KERNEL_MODE
#error This header is not supported in kernel-mode.
#endif
#include <winreg.h>
#include <new.h> // new(std::nothrow)
#include "resource.h" // unique_hkey
namespace wil
{
//! The key name includes the absolute path of the key in the registry, always starting at a
//! base key, for example, HKEY_LOCAL_MACHINE.
size_t const max_registry_key_name_length = 255;
//! The maximum number of characters allowed in a registry value's name.
size_t const max_registry_value_name_length = 16383;
// unique_registry_watcher/unique_registry_watcher_nothrow/unique_registry_watcher_failfast
// These classes make it easy to execute a provided function when a
// registry key changes (optionally recursively). Specify the key
// either as a root key + path, or an open registry handle as wil::unique_hkey
// or a raw HKEY value (that will be duplicated).
//
// Example use with exceptions base error handling:
// auto watcher = wil::make_registry_watcher(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind changeKind[]
// {
// if (changeKind == RegistryChangeKind::Delete)
// {
// watcher.reset();
// }
// // invalidate cached registry data here
// });
//
// Example use with error code base error handling:
// auto watcher = wil::make_registry_watcher_nothrow(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind[]
// {
// // invalidate cached registry data here
// });
// RETURN_IF_NULL_ALLOC(watcher);
enum class RegistryChangeKind
{
Modify = 0,
Delete = 1,
};
/// @cond
namespace details
{
struct registry_watcher_state
{
registry_watcher_state(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
: m_callback(wistd::move(callback)), m_keyToWatch(wistd::move(keyToWatch)), m_isRecursive(isRecursive)
{
}
wistd::function<void(RegistryChangeKind)> m_callback;
unique_hkey m_keyToWatch;
unique_event_nothrow m_eventHandle;
// While not strictly needed since this is ref counted the thread pool wait
// should be last to ensure that the other members are valid
// when it is destructed as it will reference them.
unique_threadpool_wait m_threadPoolWait;
bool m_isRecursive;
volatile long m_refCount = 1;
srwlock m_lock;
// Returns true if the refcount can be increased from a non zero value,
// false it was zero impling that the object is in or on the way to the destructor.
// In this case ReleaseFromCallback() should not be called.
bool TryAddRef()
{
return ::InterlockedIncrement(&m_refCount) > 1;
}
void Release()
{
auto lock = m_lock.lock_exclusive();
if (0 == ::InterlockedDecrement(&m_refCount))
{
lock.reset(); // leave the lock before deleting it.
delete this;
}
}
void ReleaseFromCallback(bool rearm)
{
auto lock = m_lock.lock_exclusive();
if (0 == ::InterlockedDecrement(&m_refCount))
{
// Destroy the thread pool wait now to avoid the wait that would occur in the
// destructor. That wait would cause a deadlock since we are doing this from the callback.
::CloseThreadpoolWait(m_threadPoolWait.release());
lock.reset(); // leave the lock before deleting it.
delete this;
// Sleep(1); // Enable for testing to find use after free bugs.
}
else if (rearm)
{
::SetThreadpoolWait(m_threadPoolWait.get(), m_eventHandle.get(), nullptr);
}
}
};
inline void delete_registry_watcher_state(_In_opt_ registry_watcher_state *watcherStorage) { watcherStorage->Release(); }
typedef resource_policy<registry_watcher_state *, decltype(&details::delete_registry_watcher_state),
details::delete_registry_watcher_state, details::pointer_access_none> registry_watcher_state_resource_policy;
}
/// @endcond
template <typename storage_t, typename err_policy = err_exception_policy>
class registry_watcher_t : public storage_t
{
public:
// forward all base class constructors...
template <typename... args_t>
explicit registry_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward<args_t>(args)...) {}
// HRESULT or void error handling...
typedef typename err_policy::result result;
// Exception-based constructors
registry_watcher_t(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
create(rootKey, subKey, isRecursive, wistd::move(callback));
}
registry_watcher_t(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
create(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
}
// Pass a root key, sub key pair or use an empty string to use rootKey as the key to watch.
result create(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
// Most use will want to create the key, consider adding an option for open as a future design change.
unique_hkey keyToWatch;
HRESULT hr = HRESULT_FROM_WIN32(RegCreateKeyExW(rootKey, subKey, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToWatch, nullptr));
if (FAILED(hr))
{
return err_policy::HResult(hr);
}
return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
}
result create(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
}
private:
// Factored into a standalone function to support Clang which does not support conversion of stateless lambdas
// to __stdcall
static void __stdcall callback(PTP_CALLBACK_INSTANCE, void *context, TP_WAIT *, TP_WAIT_RESULT)
{
#ifndef __WIL_REGISTRY_CHANGE_CALLBACK_TEST
#define __WIL_REGISTRY_CHANGE_CALLBACK_TEST
#endif
__WIL_REGISTRY_CHANGE_CALLBACK_TEST
auto watcherState = static_cast<details::registry_watcher_state *>(context);
if (watcherState->TryAddRef())
{
// using auto reset event so don't need to manually reset.
// failure here is a programming error.
const LSTATUS error = RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(), watcherState->m_isRecursive,
REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC,
watcherState->m_eventHandle.get(), TRUE);
// Call the client before re-arming to ensure that multiple callbacks don't
// run concurrently.
switch (error)
{
case ERROR_SUCCESS:
case ERROR_ACCESS_DENIED:
// Normal modification: send RegistryChangeKind::Modify and re-arm.
watcherState->m_callback(RegistryChangeKind::Modify);
watcherState->ReleaseFromCallback(true);
break;
case ERROR_KEY_DELETED:
// Key deleted, send RegistryChangeKind::Delete, do not re-arm.
watcherState->m_callback(RegistryChangeKind::Delete);
watcherState->ReleaseFromCallback(false);
break;
case ERROR_HANDLE_REVOKED:
// Handle revoked. This can occur if the user session ends before
// the watcher shuts-down. Disarm silently since there is generally no way to respond.
watcherState->ReleaseFromCallback(false);
break;
default:
FAIL_FAST_HR(HRESULT_FROM_WIN32(error));
}
}
}
// This function exists to avoid template expansion of this code based on err_policy.
HRESULT create_common(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
wistd::unique_ptr<details::registry_watcher_state> watcherState(new(std::nothrow) details::registry_watcher_state(
wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
RETURN_IF_NULL_ALLOC(watcherState);
RETURN_IF_FAILED(watcherState->m_eventHandle.create());
RETURN_IF_WIN32_ERROR(RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(),
watcherState->m_isRecursive, REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC,
watcherState->m_eventHandle.get(), TRUE));
watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(&registry_watcher_t::callback, watcherState.get(), nullptr));
RETURN_LAST_ERROR_IF(!watcherState->m_threadPoolWait);
storage_t::reset(watcherState.release()); // no more failures after this, pass ownership
SetThreadpoolWait(storage_t::get()->m_threadPoolWait.get(), storage_t::get()->m_eventHandle.get(), nullptr);
return S_OK;
}
};
typedef unique_any_t<registry_watcher_t<details::unique_storage<details::registry_watcher_state_resource_policy>, err_returncode_policy>> unique_registry_watcher_nothrow;
typedef unique_any_t<registry_watcher_t<details::unique_storage<details::registry_watcher_state_resource_policy>, err_failfast_policy>> unique_registry_watcher_failfast;
inline unique_registry_watcher_nothrow make_registry_watcher_nothrow(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback) WI_NOEXCEPT
{
unique_registry_watcher_nothrow watcher;
watcher.create(rootKey, subKey, isRecursive, wistd::move(callback));
return watcher; // caller must test for success using if (watcher)
}
inline unique_registry_watcher_nothrow make_registry_watcher_nothrow(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback) WI_NOEXCEPT
{
unique_registry_watcher_nothrow watcher;
watcher.create(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
return watcher; // caller must test for success using if (watcher)
}
inline unique_registry_watcher_failfast make_registry_watcher_failfast(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
return unique_registry_watcher_failfast(rootKey, subKey, isRecursive, wistd::move(callback));
}
inline unique_registry_watcher_failfast make_registry_watcher_failfast(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
return unique_registry_watcher_failfast(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
}
#ifdef WIL_ENABLE_EXCEPTIONS
typedef unique_any_t<registry_watcher_t<details::unique_storage<details::registry_watcher_state_resource_policy>, err_exception_policy >> unique_registry_watcher;
inline unique_registry_watcher make_registry_watcher(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
return unique_registry_watcher(rootKey, subKey, isRecursive, wistd::move(callback));
}
inline unique_registry_watcher make_registry_watcher(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
return unique_registry_watcher(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
}
#endif // WIL_ENABLE_EXCEPTIONS
} // namespace wil
#endif

5984
Externals/WIL/include/wil/resource.h vendored Normal file

File diff suppressed because it is too large Load diff

1275
Externals/WIL/include/wil/result.h vendored Normal file

File diff suppressed because it is too large Load diff

5860
Externals/WIL/include/wil/result_macros.h vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,96 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
// Note: When origination is enabled by including this file, origination is done as part of the RETURN_* and THROW_* macros. Before originating
// a new error we will observe whether there is already an error payload associated with the current thread. If there is, and the HRESULTs match,
// then a new error will not be originated. Otherwise we will overwrite it with a new origination. The ABI boundary for WinRT APIs will check the
// per-thread error information. The act of checking the error clears it, so there should be minimal risk of failing to originate distinct errors
// simply because the HRESULTs match.
//
// For THROW_ macros we will examine the thread-local error storage once per throw. So typically once, with additional calls if the exception is
// caught and re-thrown.
//
// For RETURN_ macros we will have to examine the thread-local error storage once per frame as the call stack unwinds. Because error conditions
// -should- be uncommon the performance impact of checking TLS should be minimal. The more expensive part is originating the error because it must
// capture the entire stack and some additional data.
#ifndef __WIL_RESULT_ORIGINATE_INCLUDED
#define __WIL_RESULT_ORIGINATE_INCLUDED
#include "result.h"
#include <OleAuto.h> // RestrictedErrorInfo uses BSTRs :(
#include "resource.h"
#include "com.h"
#include <roerrorapi.h>
#ifndef __cplusplus_winrt // The CX runtime likes to originate errors already so we would conflict with them.
namespace wil
{
namespace details
{
// Note: The name must begin with "Raise" so that the !analyze auto-bucketing will ignore this stack frame. Otherwise this line of code gets all the blame.
inline void __stdcall RaiseRoOriginateOnWilExceptions(wil::FailureInfo const& failure) WI_NOEXCEPT
{
if ((failure.type == FailureType::Return) || (failure.type == FailureType::Exception))
{
bool shouldOriginate = true;
wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK)
{
// This thread already has an error origination payload. Don't originate again if it has the same HRESULT that we are
// observing right now.
wil::unique_bstr descriptionUnused;
HRESULT existingHr = failure.hr;
wil::unique_bstr restrictedDescriptionUnused;
wil::unique_bstr capabilitySidUnused;
if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)))
{
shouldOriginate = (failure.hr != existingHr);
}
}
if (shouldOriginate)
{
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
wil::unique_hmodule errorModule;
if (GetModuleHandleExW(0, L"api-ms-win-core-winrt-error-l1-1-1.dll", &errorModule))
{
auto pfn = reinterpret_cast<decltype(&::RoOriginateError)>(GetProcAddress(errorModule.get(), "RoOriginateError"));
if (pfn != nullptr)
{
pfn(failure.hr, nullptr);
}
}
#else // DESKTOP | SYSTEM
::RoOriginateError(failure.hr, nullptr);
#endif // DESKTOP | SYSTEM
}
else if (restrictedErrorInformation)
{
// GetRestrictedErrorInfo returns ownership of the error information. If we aren't originating, and an error was already present,
// then we need to restore the error information for later observation.
SetRestrictedErrorInfo(restrictedErrorInformation.get());
}
}
}
} // namespace details
} // namespace wil
// Automatically call RoOriginateError upon error origination by including this file
WI_HEADER_INITITALIZATION_FUNCTION(ResultStowedExceptionInitialize, []
{
::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions);
return 1;
});
#endif // __cplusplus_winrt
#endif // __WIL_RESULT_ORIGINATE_INCLUDED

206
Externals/WIL/include/wil/rpc_helpers.h vendored Normal file
View file

@ -0,0 +1,206 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_RPC_HELPERS_INCLUDED
#define __WIL_RPC_HELPERS_INCLUDED
#include "result.h"
#include "resource.h"
#include "wistd_functional.h"
#include "wistd_type_traits.h"
namespace wil
{
/// @cond
namespace details
{
// This call-adapter template converts a void-returning 'wistd::invoke' into
// an HRESULT-returning 'wistd::invoke' that emits S_OK. It can be eliminated
// with 'if constexpr' when C++17 is in wide use.
template<typename TReturnType> struct call_adapter
{
template<typename... TArgs> static HRESULT call(TArgs&& ... args)
{
return wistd::invoke(wistd::forward<TArgs>(args)...);
}
};
template<> struct call_adapter<void>
{
template<typename... TArgs> static HRESULT call(TArgs&& ... args)
{
wistd::invoke(wistd::forward<TArgs>(args)...);
return S_OK;
}
};
// Some RPC exceptions are already HRESULTs. Others are in the regular Win32
// error space. If the incoming exception code isn't an HRESULT, wrap it.
constexpr HRESULT map_rpc_exception(DWORD code)
{
return IS_ERROR(code) ? code : __HRESULT_FROM_WIN32(code);
}
}
/// @endcond
/** Invokes an RPC method, mapping structured exceptions to HRESULTs
Failures encountered by the RPC infrastructure (such as server crashes, authentication
errors, client parameter issues, etc.) are emitted by raising a structured exception from
within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept,
RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual
flow control machinery to use.
Many RPC methods are defined as returning HRESULT themselves, where the HRESULT indicates
the result of the _work_. HRESULTs returned by a successful completion of the _call_ are
returned as-is.
RPC methods that have a return type of 'void' are mapped to returning S_OK when the _call_
completes successfully.
For example, consider an RPC interface method defined in idl as:
~~~
HRESULT GetKittenState([in, ref, string] const wchar_t* name, [out, retval] KittenState** state);
~~~
To call this method, use:
~~~
wil::unique_rpc_binding binding = // typically gotten elsewhere;
wil::unique_midl_ptr<KittenState> state;
HRESULT hr = wil::invoke_rpc_nothrow(GetKittenState, binding.get(), L"fluffy", state.put());
RETURN_IF_FAILED(hr);
~~~
*/
template<typename... TCall> HRESULT invoke_rpc_nothrow(TCall&&... args) WI_NOEXCEPT
{
RpcTryExcept
{
// Note: this helper type can be removed with C++17 enabled via
// 'if constexpr(wistd::is_same_v<void, result_t>)'
using result_t = typename wistd::__invoke_of<TCall...>::type;
RETURN_IF_FAILED(details::call_adapter<result_t>::call(wistd::forward<TCall>(args)...));
return S_OK;
}
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
{
RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
}
RpcEndExcept
}
/** Invokes an RPC method, mapping structured exceptions to HRESULTs
Failures encountered by the RPC infrastructure (such as server crashes, authentication
errors, client parameter issues, etc.) are emitted by raising a structured exception from
within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept,
RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual
flow control machinery to use.
Some RPC methods return results (such as a state enumeration or other value) directly in
their signature. This adapter writes that result into a caller-provided object then
returns S_OK.
For example, consider an RPC interface method defined in idl as:
~~~
GUID GetKittenId([in, ref, string] const wchar_t* name);
~~~
To call this method, use:
~~~
wil::unique_rpc_binding binding = // typically gotten elsewhere;
GUID id;
HRESULT hr = wil::invoke_rpc_result_nothrow(id, GetKittenId, binding.get(), L"fluffy");
RETURN_IF_FAILED(hr);
~~~
*/
template<typename TResult, typename... TCall> HRESULT invoke_rpc_result_nothrow(TResult& result, TCall&&... args) WI_NOEXCEPT
{
RpcTryExcept
{
result = wistd::invoke(wistd::forward<TCall>(args)...);
return S_OK;
}
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
{
RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
}
RpcEndExcept
}
namespace details
{
// Provides an adapter around calling the context-handle-close method on an
// RPC interface, which itself is an RPC call.
template<typename TStorage, typename close_fn_t, close_fn_t close_fn>
struct rpc_closer_t
{
static void Close(TStorage arg) WI_NOEXCEPT
{
LOG_IF_FAILED(invoke_rpc_nothrow(close_fn, &arg));
}
};
}
/** Manages explicit RPC context handles
Explicit RPC context handles are used in many RPC interfaces. Most interfaces with
context handles have an explicit `FooClose([in, out] CONTEXT*)` method that lets
the server close out the context handle. As the close method itself is an RPC call,
it can fail and raise a structured exception.
This type routes the context-handle-specific `Close` call through the `invoke_rpc_nothrow`
helper, ensuring correct cleanup and lifecycle management.
~~~
// Assume the interface has two methods:
// HRESULT OpenFoo([in] handle_t binding, [out] FOO_CONTEXT*);
// HRESULT UseFoo([in] FOO_CONTEXT context;
// void CloseFoo([in, out] PFOO_CONTEXT);
using unique_foo_context = wil::unique_rpc_context_handle<FOO_CONTEXT, decltype(&CloseFoo), CloseFoo>;
unique_foo_context context;
RETURN_IF_FAILED(wil::invoke_rpc_nothrow(OpenFoo, m_binding.get(), context.put()));
RETURN_IF_FAILED(wil::invoke_rpc_nothrow(UseFoo, context.get()));
context.reset();
~~~
*/
template<typename TContext, typename close_fn_t, close_fn_t close_fn>
using unique_rpc_context_handle = unique_any<TContext, decltype(&details::rpc_closer_t<TContext, close_fn_t, close_fn>::Close), details::rpc_closer_t<TContext, close_fn_t, close_fn>::Close>;
#ifdef WIL_ENABLE_EXCEPTIONS
/** Invokes an RPC method, mapping structured exceptions to C++ exceptions
See `wil::invoke_rpc_nothrow` for additional information. Failures during the _call_
and those returned by the _method_ are mapped to HRESULTs and thrown inside a
wil::ResultException. Using the example RPC method provided above:
~~~
wil::unique_midl_ptr<KittenState> state;
wil::invoke_rpc(GetKittenState, binding.get(), L"fluffy", state.put());
// use 'state'
~~~
*/
template<typename... TCall> void invoke_rpc(TCall&& ... args)
{
THROW_IF_FAILED(invoke_rpc_nothrow(wistd::forward<TCall>(args)...));
}
/** Invokes an RPC method, mapping structured exceptions to C++ exceptions
See `wil::invoke_rpc_result_nothrow` for additional information. Failures during the
_call_ are mapped to HRESULTs and thrown inside a `wil::ResultException`. Using the
example RPC method provided above:
~~~
GUID id = wil::invoke_rpc_result(GetKittenId, binding.get());
// use 'id'
~~~
*/
template<typename... TCall> auto invoke_rpc_result(TCall&& ... args)
{
using result_t = typename wistd::__invoke_of<TCall...>::type;
result_t result{};
THROW_IF_FAILED(invoke_rpc_result_nothrow(result, wistd::forward<TCall>(args)...));
return result;
}
#endif
}
#endif

369
Externals/WIL/include/wil/safecast.h vendored Normal file
View file

@ -0,0 +1,369 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_SAFECAST_INCLUDED
#define __WIL_SAFECAST_INCLUDED
#include "result_macros.h"
#include <intsafe.h>
#include "wistd_config.h"
#include "wistd_type_traits.h"
namespace wil
{
namespace details
{
// Default error case for undefined conversions in intsafe.h
template<typename OldT, typename NewT> constexpr wistd::nullptr_t intsafe_conversion = nullptr;
// is_known_safe_static_cast_v determines if a conversion is known to be safe or not. Known
// safe conversions can be handled by static_cast, this includes conversions between the same
// type, when the new type is larger than the old type but is not a signed to unsigned
// conversion, and when the two types are the same size and signed/unsigned. All other
// conversions will be assumed to be potentially unsafe, and the conversion must be handled
// by intsafe and checked.
template <typename NewT, typename OldT>
constexpr bool is_known_safe_static_cast_v =
(sizeof(NewT) > sizeof(OldT) && !(wistd::is_signed_v<OldT> && wistd::is_unsigned_v<NewT>)) ||
(sizeof(NewT) == sizeof(OldT) && ((wistd::is_signed_v<NewT> && wistd::is_signed_v<OldT>) || (wistd::is_unsigned_v<NewT> && wistd::is_unsigned_v<OldT>)));
// Helper template to determine that NewT and OldT are both integral types. The safe_cast
// operation only supports conversions between integral types.
template <typename NewT, typename OldT>
constexpr bool both_integral_v = wistd::is_integral<NewT>::value && wistd::is_integral<OldT>::value;
// Note on native wchar_t (__wchar_t):
// Intsafe.h does not currently handle native wchar_t. When compiling with /Zc:wchar_t-, this is fine as wchar_t is
// typedef'd to unsigned short. However, when compiling with /Zc:wchar_t or wchar_t as a native type, the lack of
// support for native wchar_t in intsafe.h becomes an issue. To work around this, we treat native wchar_t as an
// unsigned short when passing it to intsafe.h, because the two on the Windows platform are the same size and
// share the same range according to MSDN. If the cast is to a native wchar_t, the result from intsafe.h is cast
// to a native wchar_t.
// Intsafe does not have a defined conversion for native wchar_t
template <typename NewT, typename OldT>
constexpr bool neither_native_wchar_v = !wistd::is_same<NewT, __wchar_t>::value && !wistd::is_same<OldT, __wchar_t>::value;
// Check to see if the cast is a conversion to native wchar_t
template <typename NewT, typename OldT>
constexpr bool is_cast_to_wchar_v = wistd::is_same<NewT, __wchar_t>::value && !wistd::is_same<OldT, __wchar_t>::value;
// Check to see if the cast is a conversion from native wchar_t
template <typename NewT, typename OldT>
constexpr bool is_cast_from_wchar_v = !wistd::is_same<NewT, __wchar_t>::value && wistd::is_same<OldT, __wchar_t>::value;
// Validate the conversion to be performed has a defined mapping to an intsafe conversion
template <typename NewT, typename OldT>
constexpr bool is_supported_intsafe_cast_v = intsafe_conversion<OldT, NewT> != nullptr;
// True when the conversion is between integral types and can be handled by static_cast
template <typename NewT, typename OldT>
constexpr bool is_supported_safe_static_cast_v = both_integral_v<NewT, OldT> && is_known_safe_static_cast_v<NewT, OldT>;
// True when the conversion is between integral types, does not involve native wchar, has
// a mapped intsafe conversion, and is unsafe.
template <typename NewT, typename OldT>
constexpr bool is_supported_unsafe_cast_no_wchar_v =
both_integral_v<NewT, OldT> &&
!is_known_safe_static_cast_v<NewT, OldT> &&
neither_native_wchar_v<NewT, OldT> &&
is_supported_intsafe_cast_v<NewT, OldT>;
// True when the conversion is between integral types, is a cast to native wchar_t, has
// a mapped intsafe conversion, and is unsafe.
template <typename NewT, typename OldT>
constexpr bool is_supported_unsafe_cast_to_wchar_v =
both_integral_v<NewT, OldT> &&
!is_known_safe_static_cast_v<NewT, OldT> &&
is_cast_to_wchar_v<NewT, OldT> &&
is_supported_intsafe_cast_v<unsigned short, OldT>;
// True when the conversion is between integral types, is a cast from native wchar_t, has
// a mapped intsafe conversion, and is unsafe.
template <typename NewT, typename OldT>
constexpr bool is_supported_unsafe_cast_from_wchar_v =
both_integral_v<NewT, OldT> &&
!is_known_safe_static_cast_v<NewT, OldT> &&
is_cast_from_wchar_v<NewT, OldT> &&
is_supported_intsafe_cast_v<NewT, unsigned short>;
// True when the conversion is supported and unsafe, and may or may not involve
// native wchar_t.
template <typename NewT, typename OldT>
constexpr bool is_supported_unsafe_cast_v =
is_supported_unsafe_cast_no_wchar_v<NewT, OldT> ||
is_supported_unsafe_cast_to_wchar_v<NewT, OldT> ||
is_supported_unsafe_cast_from_wchar_v<NewT, OldT>;
// True when T is any one of the primitive types that the variably sized types are defined as.
template <typename T>
constexpr bool is_potentially_variably_sized_type_v =
wistd::is_same<T, int>::value ||
wistd::is_same<T, unsigned int>::value ||
wistd::is_same<T, long>::value ||
wistd::is_same<T, unsigned long>::value ||
wistd::is_same<T, __int64>::value ||
wistd::is_same<T, unsigned __int64>::value;
// True when either type is potentialy variably sized (e.g. size_t, ptrdiff_t)
template <typename OldT, typename NewT>
constexpr bool is_potentially_variably_sized_cast_v =
is_potentially_variably_sized_type_v<OldT> ||
is_potentially_variably_sized_type_v<NewT>;
// Mappings of all conversions defined in intsafe.h to intsafe_conversion
// Note: Uppercase types (UINT, DWORD, SIZE_T, etc) and architecture dependent types resolve
// to the base types. The base types are used since they do not vary based on architecture.
template<> constexpr auto intsafe_conversion<__int64, char> = LongLongToChar;
template<> constexpr auto intsafe_conversion<__int64, int> = LongLongToInt;
template<> constexpr auto intsafe_conversion<__int64, long> = LongLongToLong;
template<> constexpr auto intsafe_conversion<__int64, short> = LongLongToShort;
template<> constexpr auto intsafe_conversion<__int64, signed char> = LongLongToInt8;
template<> constexpr auto intsafe_conversion<__int64, unsigned __int64> = LongLongToULongLong;
template<> constexpr auto intsafe_conversion<__int64, unsigned char> = LongLongToUChar;
template<> constexpr auto intsafe_conversion<__int64, unsigned int> = LongLongToUInt;
template<> constexpr auto intsafe_conversion<__int64, unsigned long> = LongLongToULong;
template<> constexpr auto intsafe_conversion<__int64, unsigned short> = LongLongToUShort;
template<> constexpr auto intsafe_conversion<int, char> = IntToChar;
template<> constexpr auto intsafe_conversion<int, short> = IntToShort;
template<> constexpr auto intsafe_conversion<int, signed char> = IntToInt8;
template<> constexpr auto intsafe_conversion<int, unsigned __int64> = IntToULongLong;
template<> constexpr auto intsafe_conversion<int, unsigned char> = IntToUChar;
template<> constexpr auto intsafe_conversion<int, unsigned int> = IntToUInt;
template<> constexpr auto intsafe_conversion<int, unsigned long> = IntToULong;
template<> constexpr auto intsafe_conversion<int, unsigned short> = IntToUShort;
template<> constexpr auto intsafe_conversion<long, char> = LongToChar;
template<> constexpr auto intsafe_conversion<long, int> = LongToInt;
template<> constexpr auto intsafe_conversion<long, short> = LongToShort;
template<> constexpr auto intsafe_conversion<long, signed char> = LongToInt8;
template<> constexpr auto intsafe_conversion<long, unsigned __int64> = LongToULongLong;
template<> constexpr auto intsafe_conversion<long, unsigned char> = LongToUChar;
template<> constexpr auto intsafe_conversion<long, unsigned int> = LongToUInt;
template<> constexpr auto intsafe_conversion<long, unsigned long> = LongToULong;
template<> constexpr auto intsafe_conversion<long, unsigned short> = LongToUShort;
template<> constexpr auto intsafe_conversion<short, char> = ShortToChar;
template<> constexpr auto intsafe_conversion<short, signed char> = ShortToInt8;
template<> constexpr auto intsafe_conversion<short, unsigned __int64> = ShortToULongLong;
template<> constexpr auto intsafe_conversion<short, unsigned char> = ShortToUChar;
template<> constexpr auto intsafe_conversion<short, unsigned int> = ShortToUInt;
template<> constexpr auto intsafe_conversion<short, unsigned long> = ShortToULong;
template<> constexpr auto intsafe_conversion<short, unsigned short> = ShortToUShort;
template<> constexpr auto intsafe_conversion<signed char, unsigned __int64> = Int8ToULongLong;
template<> constexpr auto intsafe_conversion<signed char, unsigned char> = Int8ToUChar;
template<> constexpr auto intsafe_conversion<signed char, unsigned int> = Int8ToUInt;
template<> constexpr auto intsafe_conversion<signed char, unsigned long> = Int8ToULong;
template<> constexpr auto intsafe_conversion<signed char, unsigned short> = Int8ToUShort;
template<> constexpr auto intsafe_conversion<unsigned __int64, __int64> = ULongLongToLongLong;
template<> constexpr auto intsafe_conversion<unsigned __int64, char> = ULongLongToChar;
template<> constexpr auto intsafe_conversion<unsigned __int64, int> = ULongLongToInt;
template<> constexpr auto intsafe_conversion<unsigned __int64, long> = ULongLongToLong;
template<> constexpr auto intsafe_conversion<unsigned __int64, short> = ULongLongToShort;
template<> constexpr auto intsafe_conversion<unsigned __int64, signed char> = ULongLongToInt8;
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned char> = ULongLongToUChar;
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned int> = ULongLongToUInt;
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned long> = ULongLongToULong;
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned short> = ULongLongToUShort;
template<> constexpr auto intsafe_conversion<unsigned char, char> = UInt8ToChar;
template<> constexpr auto intsafe_conversion<unsigned char, signed char> = UIntToInt8;
template<> constexpr auto intsafe_conversion<unsigned int, char> = UIntToChar;
template<> constexpr auto intsafe_conversion<unsigned int, int> = UIntToInt;
template<> constexpr auto intsafe_conversion<unsigned int, long> = UIntToLong;
template<> constexpr auto intsafe_conversion<unsigned int, short> = UIntToShort;
template<> constexpr auto intsafe_conversion<unsigned int, signed char> = UIntToInt8;
template<> constexpr auto intsafe_conversion<unsigned int, unsigned char> = UIntToUChar;
template<> constexpr auto intsafe_conversion<unsigned int, unsigned short> = UIntToUShort;
template<> constexpr auto intsafe_conversion<unsigned long, char> = ULongToChar;
template<> constexpr auto intsafe_conversion<unsigned long, int> = ULongToInt;
template<> constexpr auto intsafe_conversion<unsigned long, long> = ULongToLong;
template<> constexpr auto intsafe_conversion<unsigned long, short> = ULongToShort;
template<> constexpr auto intsafe_conversion<unsigned long, signed char> = ULongToInt8;
template<> constexpr auto intsafe_conversion<unsigned long, unsigned char> = ULongToUChar;
template<> constexpr auto intsafe_conversion<unsigned long, unsigned int> = ULongToUInt;
template<> constexpr auto intsafe_conversion<unsigned long, unsigned short> = ULongToUShort;
template<> constexpr auto intsafe_conversion<unsigned short, char> = UShortToChar;
template<> constexpr auto intsafe_conversion<unsigned short, short> = UShortToShort;
template<> constexpr auto intsafe_conversion<unsigned short, signed char> = UShortToInt8;
template<> constexpr auto intsafe_conversion<unsigned short, unsigned char> = UShortToUChar;
}
// Unsafe conversion where failure results in fail fast.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast_failfast(const OldT var)
{
NewT newVar;
FAIL_FAST_IF_FAILED((details::intsafe_conversion<OldT, NewT>(var, &newVar)));
return newVar;
}
// Unsafe conversion where failure results in fail fast.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast_failfast(const OldT var)
{
NewT newVar;
FAIL_FAST_IF_FAILED((details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), &newVar)));
return newVar;
}
// Unsafe conversion where failure results in fail fast.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast_failfast(const OldT var)
{
unsigned short newVar;
FAIL_FAST_IF_FAILED((details::intsafe_conversion<OldT, unsigned short>(var, &newVar)));
return static_cast<__wchar_t>(newVar);
}
// This conversion is always safe, therefore a static_cast is fine.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
>
NewT safe_cast_failfast(const OldT var)
{
return static_cast<NewT>(var);
}
#ifdef WIL_ENABLE_EXCEPTIONS
// Unsafe conversion where failure results in a thrown exception.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast(const OldT var)
{
NewT newVar;
THROW_IF_FAILED((details::intsafe_conversion<OldT, NewT>(var, &newVar)));
return newVar;
}
// Unsafe conversion where failure results in a thrown exception.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast(const OldT var)
{
NewT newVar;
THROW_IF_FAILED((details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), &newVar)));
return newVar;
}
// Unsafe conversion where failure results in a thrown exception.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
>
NewT safe_cast(const OldT var)
{
unsigned short newVar;
THROW_IF_FAILED((details::intsafe_conversion<OldT, unsigned short>(var, &newVar)));
return static_cast<__wchar_t>(newVar);
}
// This conversion is always safe, therefore a static_cast is fine.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
>
NewT safe_cast(const OldT var)
{
return static_cast<NewT>(var);
}
#endif
// This conversion is unsafe, therefore the two parameter version of safe_cast_nothrow must be used
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_v<NewT, OldT>, int> = 0
>
NewT safe_cast_nothrow(const OldT /*var*/)
{
static_assert(!wistd::is_same_v<NewT, NewT>, "This cast has the potential to fail, use the two parameter safe_cast_nothrow instead");
}
// This conversion is always safe, therefore a static_cast is fine.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
>
NewT safe_cast_nothrow(const OldT var)
{
return static_cast<NewT>(var);
}
// Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
>
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
{
return details::intsafe_conversion<OldT, NewT>(var, newTResult);
}
// Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
>
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
{
return details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), newTResult);
}
// Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
>
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
{
return details::intsafe_conversion<OldT, unsigned short>(var, reinterpret_cast<unsigned short *>(newTResult));
}
// This conversion is always safe, therefore a static_cast is fine. If it can be determined the conversion
// does not involve a variably sized type, then the compilation will fail and say the single parameter version
// of safe_cast_nothrow should be used instead.
template <
typename NewT,
typename OldT,
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
>
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
{
static_assert(details::is_potentially_variably_sized_cast_v<OldT, NewT>, "This cast is always safe; use safe_cast_nothrow<T>(value) to avoid unnecessary error handling.");
*newTResult = static_cast<NewT>(var);
return S_OK;
}
}
#endif // __WIL_SAFECAST_INCLUDED

124
Externals/WIL/include/wil/stl.h vendored Normal file
View file

@ -0,0 +1,124 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_STL_INCLUDED
#define __WIL_STL_INCLUDED
#include "common.h"
#include "resource.h"
#include <memory>
#include <string>
#if defined(WIL_ENABLE_EXCEPTIONS)
namespace std
{
template<class _Ty, class _Alloc>
class vector;
template<class _Elem>
struct char_traits;
template<class _Elem, class _Traits, class _Alloc>
class basic_string;
} // namespace std
namespace wil
{
/** Secure allocator for STL containers.
The `wil::secure_allocator` allocator calls `SecureZeroMemory` before deallocating
memory. This provides a mechanism for secure STL containers such as `wil::secure_vector`,
`wil::secure_string`, and `wil::secure_wstring`. */
template <typename T>
struct secure_allocator
: public std::allocator<T>
{
template<typename Other>
struct rebind
{
typedef secure_allocator<Other> other;
};
secure_allocator()
: std::allocator<T>()
{
}
~secure_allocator() = default;
secure_allocator(const secure_allocator& a)
: std::allocator<T>(a)
{
}
template <class U>
secure_allocator(const secure_allocator<U>& a)
: std::allocator<T>(a)
{
}
T* allocate(size_t n)
{
return std::allocator<T>::allocate(n);
}
void deallocate(T* p, size_t n)
{
SecureZeroMemory(p, sizeof(T) * n);
std::allocator<T>::deallocate(p, n);
}
};
//! `wil::secure_vector` will be securely zeroed before deallocation.
template <typename Type>
using secure_vector = std::vector<Type, secure_allocator<Type>>;
//! `wil::secure_wstring` will be securely zeroed before deallocation.
using secure_wstring = std::basic_string<wchar_t, std::char_traits<wchar_t>, wil::secure_allocator<wchar_t>>;
//! `wil::secure_string` will be securely zeroed before deallocation.
using secure_string = std::basic_string<char, std::char_traits<char>, wil::secure_allocator<char>>;
/// @cond
namespace details
{
template<> struct string_maker<std::wstring>
{
HRESULT make(_In_reads_opt_(length) PCWSTR source, size_t length) WI_NOEXCEPT try
{
m_value = source ? std::wstring(source, length) : std::wstring(length, L'\0');
return S_OK;
}
catch (...)
{
return E_OUTOFMEMORY;
}
wchar_t* buffer() { return &m_value[0]; }
std::wstring release() { return std::wstring(std::move(m_value)); }
static PCWSTR get(const std::wstring& value) { return value.c_str(); }
private:
std::wstring m_value;
};
}
/// @endcond
// str_raw_ptr is an overloaded function that retrieves a const pointer to the first character in a string's buffer.
// This is the overload for std::wstring. Other overloads available in resource.h.
inline PCWSTR str_raw_ptr(const std::wstring& str)
{
return str.c_str();
}
} // namespace wil
#endif // WIL_ENABLE_EXCEPTIONS
#endif // __WIL_STL_INCLUDED

View file

@ -0,0 +1,597 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_TOKEN_HELPERS_INCLUDED
#define __WIL_TOKEN_HELPERS_INCLUDED
#ifdef _KERNEL_MODE
#error This header is not supported in kernel-mode.
#endif
#include "resource.h"
#include <new>
#include <lmcons.h> // for UNLEN and DNLEN
#include <processthreadsapi.h>
// for GetUserNameEx()
#define SECURITY_WIN32
#include <Security.h>
namespace wil
{
/// @cond
namespace details
{
// Template specialization for TOKEN_INFORMATION_CLASS, add more mappings here as needed
// TODO: The mapping should be reversed to be MapTokenInfoClassToStruct since there may
// be an info class value that uses the same structure. That is the case for the file
// system information.
template<typename T> struct MapTokenStructToInfoClass;
template<> struct MapTokenStructToInfoClass<TOKEN_ACCESS_INFORMATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenAccessInformation; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_APPCONTAINER_INFORMATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenAppContainerSid; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_DEFAULT_DACL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenDefaultDacl; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_GROUPS_AND_PRIVILEGES> { static const TOKEN_INFORMATION_CLASS infoClass = TokenGroupsAndPrivileges; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_MANDATORY_LABEL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenIntegrityLevel; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_OWNER> { static const TOKEN_INFORMATION_CLASS infoClass = TokenOwner; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_PRIMARY_GROUP> { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrimaryGroup; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_PRIVILEGES> { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrivileges; static const bool FixedSize = false; };
template<> struct MapTokenStructToInfoClass<TOKEN_USER> { static const TOKEN_INFORMATION_CLASS infoClass = TokenUser; static const bool FixedSize = false; };
// fixed size cases
template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION_TYPE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevationType; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_MANDATORY_POLICY> { static const TOKEN_INFORMATION_CLASS infoClass = TokenMandatoryPolicy; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_ORIGIN> { static const TOKEN_INFORMATION_CLASS infoClass = TokenOrigin; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_SOURCE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenSource; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_STATISTICS> { static const TOKEN_INFORMATION_CLASS infoClass = TokenStatistics; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_TYPE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenType; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<SECURITY_IMPERSONATION_LEVEL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenImpersonationLevel; static const bool FixedSize = true; };
template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevation; static const bool FixedSize = true; };
}
/// @endcond
enum class OpenThreadTokenAs
{
Current,
Self
};
/** Open the active token.
Opens either the current thread token (if impersonating) or the current process token. Returns a token the caller
can use with methods like get_token_information<> below. By default, the token is opened for TOKEN_QUERY and as the
effective user.
Consider using GetCurrentThreadEffectiveToken() instead of this method when eventually calling get_token_information.
This method returns a real handle to the effective token, but GetCurrentThreadEffectiveToken() is a Pseudo-handle
and much easier to manage.
~~~~
wil::unique_handle theToken;
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken));
~~~~
Callers who want more access to the token (such as to duplicate or modify the token) can pass
any mask of the token rights.
~~~~
wil::unique_handle theToken;
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES));
~~~~
Services impersonating their clients may need to request that the active token is opened on the
behalf of the service process to perform certain operations. Opening a token for impersonation access
or privilege-adjustment are examples of uses.
~~~~
wil::unique_handle callerToken;
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_IMPERSONATE, true));
~~~~
@param tokenHandle Receives the token opened during the operation. Must be CloseHandle'd by the caller, or
(preferably) stored in a wil::unique_handle
@param access Bits from the TOKEN_* access mask which are passed to OpenThreadToken/OpenProcessToken
@param asSelf When true, and if the thread is impersonating, the thread token is opened using the
process token's rights.
*/
inline HRESULT open_current_access_token_nothrow(_Out_ HANDLE* tokenHandle, unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
{
HRESULT hr = (OpenThreadToken(GetCurrentThread(), access, (openAs == OpenThreadTokenAs::Self), tokenHandle) ? S_OK : HRESULT_FROM_WIN32(::GetLastError()));
if (hr == HRESULT_FROM_WIN32(ERROR_NO_TOKEN))
{
hr = (OpenProcessToken(GetCurrentProcess(), access, tokenHandle) ? S_OK : HRESULT_FROM_WIN32(::GetLastError()));
}
return hr;
}
//! Current thread or process token, consider using GetCurrentThreadEffectiveToken() instead.
inline wil::unique_handle open_current_access_token_failfast(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
{
HANDLE rawTokenHandle;
FAIL_FAST_IF_FAILED(open_current_access_token_nothrow(&rawTokenHandle, access, openAs));
return wil::unique_handle(rawTokenHandle);
}
// Exception based function to open current thread/process access token and acquire pointer to it
#ifdef WIL_ENABLE_EXCEPTIONS
//! Current thread or process token, consider using GetCurrentThreadEffectiveToken() instead.
inline wil::unique_handle open_current_access_token(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
{
HANDLE rawTokenHandle;
THROW_IF_FAILED(open_current_access_token_nothrow(&rawTokenHandle, access, openAs));
return wil::unique_handle(rawTokenHandle);
}
#endif // WIL_ENABLE_EXCEPTIONS
// Returns tokenHandle or the effective thread token if tokenHandle is null.
// Note, this returns an token handle who's lifetime is managed independently
// and it may be a pseudo token, don't free it!
inline HANDLE GetCurrentThreadEffectiveTokenWithOverride(HANDLE tokenHandle)
{
return tokenHandle ? tokenHandle : GetCurrentThreadEffectiveToken();
}
/** Fetches information about a token.
See GetTokenInformation on MSDN for what this method can return. For variable sized structs the information
is returned to the caller as a wistd::unique_ptr<T> (like TOKEN_ORIGIN, TOKEN_USER, TOKEN_ELEVATION, etc.). For
fixed sized, the struct is returned directly.
The caller must have access to read the information from the provided token. This method works with both real
(e.g. OpenCurrentAccessToken) and pseudo (e.g. GetCurrentThreadToken) token handles.
~~~~
// Retrieve the TOKEN_USER structure for the current process
wistd::unique_ptr<TOKEN_USER> user;
RETURN_IF_FAILED(wil::get_token_information_nothrow(user, GetCurrentProcessToken()));
RETURN_IF_FAILED(ConsumeSid(user->User.Sid));
~~~~
Not specifying the token handle is the same as specifying 'nullptr' and retrieves information about the effective token.
~~~~
wistd::unique_ptr<TOKEN_PRIVILEGES> privileges;
RETURN_IF_FAILED(wil::get_token_information_nothrow(privileges));
for (auto const& privilege : wil::GetRange(privileges->Privileges, privileges->PrivilegeCount))
{
RETURN_IF_FAILED(ConsumePrivilege(privilege));
}
~~~~
@param tokenInfo Receives a pointer to a structure containing the results of GetTokenInformation for the requested
type. The type of <T> selects which TOKEN_INFORMATION_CLASS will be used.
@param tokenHandle Specifies which token will be queried. When nullptr, the thread's effective current token is used.
@return S_OK on success, a FAILED hresult containing the win32 error from querying the token otherwise.
*/
template <typename T, wistd::enable_if_t<!details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
inline HRESULT get_token_information_nothrow(wistd::unique_ptr<T>& tokenInfo, HANDLE tokenHandle = nullptr)
{
tokenInfo.reset();
tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle);
DWORD tokenInfoSize = 0;
const auto infoClass = details::MapTokenStructToInfoClass<T>::infoClass;
RETURN_LAST_ERROR_IF(!((!GetTokenInformation(tokenHandle, infoClass, nullptr, 0, &tokenInfoSize)) &&
(::GetLastError() == ERROR_INSUFFICIENT_BUFFER)));
wistd::unique_ptr<char> tokenInfoClose(
static_cast<char*>(operator new(tokenInfoSize, std::nothrow)));
RETURN_IF_NULL_ALLOC(tokenInfoClose.get());
RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfoClose.get(), tokenInfoSize, &tokenInfoSize));
tokenInfo.reset(reinterpret_cast<T *>(tokenInfoClose.release()));
return S_OK;
}
template <typename T, wistd::enable_if_t<details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
inline HRESULT get_token_information_nothrow(_Out_ T* tokenInfo, HANDLE tokenHandle = nullptr)
{
*tokenInfo = {};
tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle);
DWORD tokenInfoSize = sizeof(T);
const auto infoClass = details::MapTokenStructToInfoClass<T>::infoClass;
RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfo, tokenInfoSize, &tokenInfoSize));
return S_OK;
}
namespace details
{
template<typename T, typename policy, wistd::enable_if_t<!details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
wistd::unique_ptr<T> GetTokenInfoWrap(HANDLE token = nullptr)
{
wistd::unique_ptr<T> temp;
policy::HResult(get_token_information_nothrow(temp, token));
return temp;
}
template<typename T, typename policy, wistd::enable_if_t<details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
T GetTokenInfoWrap(HANDLE token = nullptr)
{
T temp{};
policy::HResult(get_token_information_nothrow(&temp, token));
return temp;
}
}
//! A variant of get_token_information<T> that fails-fast on errors retrieving the token
template <typename T>
inline auto get_token_information_failfast(HANDLE token = nullptr)
{
return details::GetTokenInfoWrap<T, err_failfast_policy>(token);
}
//! Overload of GetTokenInformationNoThrow that retrieves a token linked from the provided token
inline HRESULT get_token_information_nothrow(unique_token_linked_token& tokenInfo, HANDLE tokenHandle = nullptr)
{
static_assert(sizeof(tokenInfo) == sizeof(TOKEN_LINKED_TOKEN), "confusing size mismatch");
tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle);
DWORD tokenInfoSize = 0;
RETURN_IF_WIN32_BOOL_FALSE(::GetTokenInformation(tokenHandle, TokenLinkedToken,
tokenInfo.reset_and_addressof(), sizeof(tokenInfo), &tokenInfoSize));
return S_OK;
}
/** Retrieves the linked-token information for a token.
Fails-fast if the link information cannot be retrieved.
~~~~
auto link = get_linked_token_information_failfast(GetCurrentThreadToken());
auto tokenUser = get_token_information<TOKEN_USER>(link.LinkedToken);
~~~~
@param token Specifies the token to query. Pass nullptr to use the current effective thread token
@return unique_token_linked_token containing a handle to the linked token
*/
inline unique_token_linked_token get_linked_token_information_failfast(HANDLE token = nullptr)
{
unique_token_linked_token tokenInfo;
FAIL_FAST_IF_FAILED(get_token_information_nothrow(tokenInfo, token));
return tokenInfo;
}
#ifdef WIL_ENABLE_EXCEPTIONS
/** Fetches information about a token.
See get_token_information_nothrow for full details.
~~~~
auto user = wil::get_token_information<TOKEN_USER>(GetCurrentProcessToken());
ConsumeSid(user->User.Sid);
~~~~
Pass 'nullptr' (or omit the parameter) as tokenHandle to retrieve information about the effective token.
~~~~
auto privs = wil::get_token_information<TOKEN_PRIVILEGES>(privileges);
for (auto& priv : wil::make_range(privs->Privileges, privs->Privilieges + privs->PrivilegeCount))
{
if (priv.Attributes & SE_PRIVILEGE_ENABLED)
{
// ...
}
}
~~~~
@return A pointer to a structure containing the results of GetTokenInformation for the requested type. The type of
<T> selects which TOKEN_INFORMATION_CLASS will be used.
@param token Specifies which token will be queried. When nullptr or not set, the thread's effective current token is used.
*/
template <typename T>
inline auto get_token_information(HANDLE token = nullptr)
{
return details::GetTokenInfoWrap<T, err_exception_policy>(token);
}
/** Retrieves the linked-token information for a token.
Throws an exception if the link information cannot be retrieved.
~~~~
auto link = get_linked_token_information(GetCurrentThreadToken());
auto tokenUser = get_token_information<TOKEN_USER>(link.LinkedToken);
~~~~
@param token Specifies the token to query. Pass nullptr to use the current effective thread token
@return unique_token_linked_token containing a handle to the linked token
*/
inline unique_token_linked_token get_linked_token_information(HANDLE token = nullptr)
{
unique_token_linked_token tokenInfo;
THROW_IF_FAILED(get_token_information_nothrow(tokenInfo, token));
return tokenInfo;
}
#endif
/// @cond
namespace details
{
inline void RevertImpersonateToken(_Pre_opt_valid_ _Frees_ptr_opt_ HANDLE oldToken)
{
FAIL_FAST_IMMEDIATE_IF(!::SetThreadToken(nullptr, oldToken));
if (oldToken)
{
::CloseHandle(oldToken);
}
}
}
/// @endcond
using unique_token_reverter = wil::unique_any<
HANDLE,
decltype(&details::RevertImpersonateToken),
details::RevertImpersonateToken,
details::pointer_access_none,
HANDLE,
INT_PTR,
-1,
HANDLE>;
/** Temporarily impersonates a token on this thread.
This method sets a new token on a thread, restoring the current token when the returned object
is destroyed. Useful for impersonating other tokens or running as 'self,' especially in services.
~~~~
HRESULT OpenFileAsSessionuser(PCWSTR filePath, DWORD session, _Out_ HANDLE* opened)
{
wil::unique_handle userToken;
RETURN_IF_WIN32_BOOL_FALSE(QueryUserToken(session, &userToken));
wil::unique_token_reverter reverter;
RETURN_IF_FAILED(wil::impersonate_token_nothrow(userToken.get(), reverter));
wil::unique_hfile userFile(::CreateFile(filePath, ...));
RETURN_LAST_ERROR_IF(!userFile && (::GetLastError() != ERROR_FILE_NOT_FOUND));
*opened = userFile.release();
return S_OK;
}
~~~~
@param token A token to impersonate, or 'nullptr' to run as the process identity.
*/
inline HRESULT impersonate_token_nothrow(HANDLE token, unique_token_reverter& reverter)
{
wil::unique_handle currentToken;
// Get the token for the current thread. If there wasn't one, the reset will clear it as well
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &currentToken))
{
RETURN_LAST_ERROR_IF(::GetLastError() != ERROR_NO_TOKEN);
}
// Update the current token
RETURN_IF_WIN32_BOOL_FALSE(::SetThreadToken(nullptr, token));
reverter.reset(currentToken.release()); // Ownership passed
return S_OK;
}
/** Temporarily clears any impersonation on this thread.
This method resets the current thread's token to nullptr, indicating that it is not impersonating
any user. Useful for elevating to whatever identity a service or higher-privilege process might
be capable of running under.
~~~~
HRESULT DeleteFileRetryAsSelf(PCWSTR filePath)
{
if (!::DeleteFile(filePath))
{
RETURN_LAST_ERROR_IF(::GetLastError() != ERROR_ACCESS_DENIED);
wil::unique_token_reverter reverter;
RETURN_IF_FAILED(wil::run_as_self_nothrow(reverter));
RETURN_IF_FAILED(TakeOwnershipOfFile(filePath));
RETURN_IF_FAILED(GrantDeleteAccess(filePath));
RETURN_IF_WIN32_BOOL_FALSE(::DeleteFile(filePath));
}
return S_OK;
}
~~~~
*/
inline HRESULT run_as_self_nothrow(unique_token_reverter& reverter)
{
return impersonate_token_nothrow(nullptr, reverter);
}
inline unique_token_reverter impersonate_token_failfast(HANDLE token)
{
unique_token_reverter oldToken;
FAIL_FAST_IF_FAILED(impersonate_token_nothrow(token, oldToken));
return oldToken;
}
inline unique_token_reverter run_as_self_failfast()
{
return impersonate_token_failfast(nullptr);
}
#ifdef WIL_ENABLE_EXCEPTIONS
/** Temporarily impersonates a token on this thread.
This method sets a new token on a thread, restoring the current token when the returned object
is destroyed. Useful for impersonating other tokens or running as 'self,' especially in services.
~~~~
wil::unique_hfile OpenFileAsSessionuser(_In_z_ const wchar_t* filePath, DWORD session)
{
wil::unique_handle userToken;
THROW_IF_WIN32_BOOL_FALSE(QueryUserToken(session, &userToken));
auto priorToken = wil::impersonate_token(userToken.get());
wil::unique_hfile userFile(::CreateFile(filePath, ...));
THROW_LAST_ERROR_IF(::GetLastError() != ERROR_FILE_NOT_FOUND);
return userFile;
}
~~~~
@param token A token to impersonate, or 'nullptr' to run as the process identity.
*/
inline unique_token_reverter impersonate_token(HANDLE token = nullptr)
{
unique_token_reverter oldToken;
THROW_IF_FAILED(impersonate_token_nothrow(token, oldToken));
return oldToken;
}
/** Temporarily clears any impersonation on this thread.
This method resets the current thread's token to nullptr, indicating that it is not impersonating
any user. Useful for elevating to whatever identity a service or higher-privilege process might
be capable of running under.
~~~~
void DeleteFileRetryAsSelf(_In_z_ const wchar_t* filePath)
{
if (!::DeleteFile(filePath) && (::GetLastError() == ERROR_ACCESS_DENIED))
{
auto priorToken = wil::run_as_self();
TakeOwnershipOfFile(filePath);
GrantDeleteAccess(filePath);
::DeleteFile(filePath);
}
}
~~~~
*/
inline unique_token_reverter run_as_self()
{
return impersonate_token(nullptr);
}
#endif // WIL_ENABLE_EXCEPTIONS
namespace details
{
template<size_t AuthorityCount> struct static_sid_t
{
BYTE Revision;
BYTE SubAuthorityCount;
SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
DWORD SubAuthority[AuthorityCount];
PSID get()
{
return reinterpret_cast<PSID>(this);
}
template<size_t other> static_sid_t& operator=(const static_sid_t<other>& source)
{
static_assert(other <= AuthorityCount, "Cannot assign from a larger static sid to a smaller one");
if (&this->Revision != &source.Revision)
{
memcpy(this, &source, sizeof(source));
}
return *this;
}
};
}
/** Returns a structure containing a Revision 1 SID initialized with the authorities provided
Replaces AllocateAndInitializeSid by constructing a structure laid out like a PSID, but
returned like a value. The resulting object is suitable for use with any method taking PSID,
passed by "&the_sid" or via "the_sid.get()"
~~~~
// Change the owner of the key to administrators
auto systemSid = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
RETURN_IF_WIN32_ERROR(SetNamedSecurityInfo(keyPath, SE_REGISTRY_KEY, OWNER_SECURITY_INFORMATION, &systemSid, nullptr, nullptr, nullptr));
~~~~
*/
template<typename... Ts> constexpr auto make_static_sid(const SID_IDENTIFIER_AUTHORITY& authority, Ts&&... subAuthorities)
{
using sid_t = details::static_sid_t<sizeof...(subAuthorities)>;
static_assert(sizeof...(subAuthorities) <= SID_MAX_SUB_AUTHORITIES, "too many sub authorities");
static_assert(offsetof(sid_t, Revision) == offsetof(_SID, Revision), "layout mismatch");
static_assert(offsetof(sid_t, SubAuthorityCount) == offsetof(_SID, SubAuthorityCount), "layout mismatch");
static_assert(offsetof(sid_t, IdentifierAuthority) == offsetof(_SID, IdentifierAuthority), "layout mismatch");
static_assert(offsetof(sid_t, SubAuthority) == offsetof(_SID, SubAuthority), "layout mismatch");
return sid_t { SID_REVISION, sizeof...(subAuthorities), authority, { static_cast<DWORD>(subAuthorities)... } };
}
//! Variant of static_sid that defaults to the NT authority
template<typename... Ts> constexpr auto make_static_nt_sid(Ts&& ... subAuthorities)
{
return make_static_sid(SECURITY_NT_AUTHORITY, wistd::forward<Ts>(subAuthorities)...);
}
/** Determines whether a specified security identifier (SID) is enabled in an access token.
This function determines whether a security identifier, described by a given set of subauthorities, is enabled
in the given access token. Note that only up to eight subauthorities can be passed to this function.
~~~~
bool IsGuest()
{
return wil::test_token_membership(nullptr, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS));
}
~~~~
@param result This will be set to true if and only if a security identifier described by the given set of subauthorities is enabled in the given access token.
@param token A handle to an access token. The handle must have TOKEN_QUERY access to the token, and must be an impersonation token. If token is nullptr, test_token_membership
uses the impersonation token of the calling thread. If the thread is not impersonating, the function duplicates the thread's primary token to create an impersonation token.
@param sidAuthority A reference to a SID_IDENTIFIER_AUTHORITY structure. This structure provides the top-level identifier authority value to set in the SID.
@param subAuthorities Up to 15 subauthority values to place in the SID (this is a systemwide limit)
@return S_OK on success, a FAILED hresult containing the win32 error from creating the SID or querying the token otherwise.
*/
template<typename... Ts> HRESULT test_token_membership_nothrow(_Out_ bool* result, _In_opt_ HANDLE token,
const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities)
{
*result = false;
auto tempSid = make_static_sid(sidAuthority, wistd::forward<Ts>(subAuthorities)...);
BOOL isMember;
RETURN_IF_WIN32_BOOL_FALSE(CheckTokenMembership(token, &tempSid, &isMember));
*result = (isMember != FALSE);
return S_OK;
}
/** Determine whether a token represents an app container
This method uses the passed in token and emits a boolean indicating that
whether TokenIsAppContainer is true.
~~~~
HRESULT OnlyIfAppContainer()
{
bool isAppContainer;
RETURN_IF_FAILED(wil::get_token_is_app_container_nothrow(nullptr, isAppContainer));
RETURN_HR_IF(E_ACCESSDENIED, !isAppContainer);
RETURN_HR(...);
}
~~~~
@param token A token to get info about, or 'nullptr' to run as the current thread.
*/
inline HRESULT get_token_is_app_container_nothrow(_In_opt_ HANDLE token, bool& value)
{
DWORD isAppContainer = 0;
DWORD returnLength = 0;
RETURN_IF_WIN32_BOOL_FALSE(::GetTokenInformation(
token ? token : GetCurrentThreadEffectiveToken(),
TokenIsAppContainer,
&isAppContainer,
sizeof(isAppContainer),
&returnLength));
value = (isAppContainer != 0);
return S_OK;
}
//! A variant of get_token_is_app_container_nothrow that fails-fast on errors retrieving the token information
inline bool get_token_is_app_container_failfast(HANDLE token = nullptr)
{
bool value = false;
FAIL_FAST_IF_FAILED(get_token_is_app_container_nothrow(token, value));
return value;
}
#ifdef WIL_ENABLE_EXCEPTIONS
//! A variant of get_token_is_app_container_nothrow that throws on errors retrieving the token information
inline bool get_token_is_app_container(HANDLE token = nullptr)
{
bool value = false;
THROW_IF_FAILED(get_token_is_app_container_nothrow(token, value));
return value;
}
#endif // WIL_ENABLE_EXCEPTIONS
template<typename... Ts> bool test_token_membership_failfast(_In_opt_ HANDLE token,
const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities)
{
bool result;
FAIL_FAST_IF_FAILED(test_token_membership_nothrow(&result, token, sidAuthority, wistd::forward<Ts>(subAuthorities)...));
return result;
}
#ifdef WIL_ENABLE_EXCEPTIONS
template<typename... Ts> bool test_token_membership(_In_opt_ HANDLE token, const SID_IDENTIFIER_AUTHORITY& sidAuthority,
Ts&&... subAuthorities)
{
bool result;
THROW_IF_FAILED(test_token_membership_nothrow(&result, token, sidAuthority, wistd::forward<Ts>(subAuthorities)...));
return result;
}
#endif
} //namespace wil
#endif // __WIL_TOKEN_HELPERS_INCLUDED

View file

@ -0,0 +1,563 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_WIN32_HELPERS_INCLUDED
#define __WIL_WIN32_HELPERS_INCLUDED
#include <minwindef.h> // FILETIME, HINSTANCE
#include <sysinfoapi.h> // GetSystemTimeAsFileTime
#include <libloaderapi.h> // GetProcAddress
#include <Psapi.h> // GetModuleFileNameExW (macro), K32GetModuleFileNameExW
#include <PathCch.h>
#include <objbase.h>
#include "result.h"
#include "resource.h"
#include "wistd_functional.h"
#include "wistd_type_traits.h"
namespace wil
{
//! Strictly a function of the file system but this is the value for all known file system, NTFS, FAT.
//! CDFs has a limit of 254.
size_t const max_path_segment_length = 255;
//! Character length not including the null, MAX_PATH (260) includes the null.
size_t const max_path_length = 259;
//! 32743 Character length not including the null. This is a system defined limit.
//! The 24 is for the expansion of the roots from "C:" to "\Device\HarddiskVolume4"
//! It will be 25 when there are more than 9 disks.
size_t const max_extended_path_length = 0x7FFF - 24;
//! For {guid} string form. Includes space for the null terminator.
size_t const guid_string_buffer_length = 39;
//! For {guid} string form. Not including the null terminator.
size_t const guid_string_length = 38;
#pragma region FILETIME helpers
// FILETIME duration values. FILETIME is in 100 nanosecond units.
namespace filetime_duration
{
long long const one_millisecond = 10000LL;
long long const one_second = 10000000LL;
long long const one_minute = 10000000LL * 60; // 600000000 or 600000000LL
long long const one_hour = 10000000LL * 60 * 60; // 36000000000 or 36000000000LL
long long const one_day = 10000000LL * 60 * 60 * 24; // 864000000000 or 864000000000LL
};
namespace filetime
{
inline unsigned long long to_int64(const FILETIME &ft)
{
// Cannot reinterpret_cast FILETIME* to unsigned long long*
// due to alignment differences.
return (static_cast<unsigned long long>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
}
inline FILETIME from_int64(unsigned long long i64)
{
static_assert(sizeof(i64) == sizeof(FILETIME), "sizes don't match");
static_assert(__alignof(unsigned long long) >= __alignof(FILETIME), "alignment not compatible with type pun");
return *reinterpret_cast<FILETIME *>(&i64);
}
inline FILETIME add(_In_ FILETIME const &ft, long long delta)
{
return from_int64(to_int64(ft) + delta);
}
inline bool is_empty(const FILETIME &ft)
{
return (ft.dwHighDateTime == 0) && (ft.dwLowDateTime == 0);
}
inline FILETIME get_system_time()
{
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
return ft;
}
}
#pragma endregion
// Use to adapt Win32 APIs that take a fixed size buffer into forms that return
// an allocated buffer. Supports many types of string representation.
// See comments below on the expected behavior of the callback.
// Adjust stackBufferLength based on typical result sizes to optimize use and
// to test the boundary cases.
template <typename string_type, size_t stackBufferLength = 256>
HRESULT AdaptFixedSizeToAllocatedResult(string_type& result, wistd::function<HRESULT(PWSTR, size_t, size_t*)> callback)
{
details::string_maker<string_type> maker;
wchar_t value[stackBufferLength];
value[0] = L'\0';
size_t valueLengthNeededWithNull{}; // callback returns the number of characters needed including the null terminator.
RETURN_IF_FAILED_EXPECTED(callback(value, ARRAYSIZE(value), &valueLengthNeededWithNull));
WI_ASSERT(valueLengthNeededWithNull > 0);
if (valueLengthNeededWithNull <= ARRAYSIZE(value))
{
// Success case as described above, make() adds the space for the null.
RETURN_IF_FAILED(maker.make(value, valueLengthNeededWithNull - 1));
}
else
{
// Did not fit in the stack allocated buffer, need to do 2 phase construction.
// valueLengthNeededWithNull includes the null so subtract that as make() will add space for it.
RETURN_IF_FAILED(maker.make(nullptr, valueLengthNeededWithNull - 1));
size_t secondLength{};
RETURN_IF_FAILED(callback(maker.buffer(), valueLengthNeededWithNull, &secondLength));
// Ensure callback produces consistent result.
FAIL_FAST_IF(valueLengthNeededWithNull != secondLength);
}
result = maker.release();
return S_OK;
}
/** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */
template <typename string_type, size_t stackBufferLength = 256>
HRESULT ExpandEnvironmentStringsW(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
*valueLengthNeededWithNul = ::ExpandEnvironmentStringsW(input, value, static_cast<DWORD>(valueLength));
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
return S_OK;
});
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
/** Searches for a specified file in a specified path using ExpandEnvironmentStringsW(); */
template <typename string_type, size_t stackBufferLength = 256>
HRESULT SearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, _In_opt_ PCWSTR extension, string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
*valueLengthNeededWithNul = ::SearchPathW(path, fileName, extension, static_cast<DWORD>(valueLength), value, nullptr);
if (*valueLengthNeededWithNul == 0)
{
// ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW
const HRESULT searchResult = HRESULT_FROM_WIN32(::GetLastError());
RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
RETURN_IF_FAILED(searchResult);
}
// AdaptFixedSizeToAllocatedResult expects that the length will always include the NUL.
// If the result is copied to the buffer, SearchPathW returns the length of copied string, WITHOUT the NUL.
// If the buffer is too small to hold the result, SearchPathW returns the length of the required buffer WITH the nul.
if (*valueLengthNeededWithNul < valueLength)
{
(*valueLengthNeededWithNul)++; // It fit, account for the null.
}
return S_OK;
});
}
// This function does not work beyond the default stack buffer size (255).
// Needs to to retry in a loop similar to wil::GetModuleFileNameExW
// These updates and unit tests are tracked by https://github.com/Microsoft/wil/issues/3
template <typename string_type, size_t stackBufferLength = 256>
HRESULT QueryFullProcessImageNameW(HANDLE processHandle, _In_ DWORD flags, string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
DWORD lengthToUse = static_cast<DWORD>(valueLength);
BOOL const success = ::QueryFullProcessImageNameW(processHandle, flags, value, &lengthToUse);
RETURN_LAST_ERROR_IF((success == FALSE) && (::GetLastError() != ERROR_INSUFFICIENT_BUFFER));
// On both success or insufficient buffer case, add +1 for the null-terminating character
*valueLengthNeededWithNul = lengthToUse + 1;
return S_OK;
});
}
/** Expands environment strings and checks path existence with SearchPathW */
template <typename string_type, size_t stackBufferLength = 256>
HRESULT ExpandEnvAndSearchPath(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT
{
wil::unique_cotaskmem_string expandedName;
RETURN_IF_FAILED((wil::ExpandEnvironmentStringsW<string_type, stackBufferLength>(input, expandedName)));
// ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW
const HRESULT searchResult = (wil::SearchPathW<string_type, stackBufferLength>(nullptr, expandedName.get(), nullptr, result));
RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
RETURN_IF_FAILED(searchResult);
return S_OK;
}
#endif
/** Looks up the environment variable 'key' and fails if it is not found.
'key' should not have '%' prefix and suffix.
Dangerous since environment variable generally are optional. */
template <typename string_type>
inline HRESULT GetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
// If the function succeeds, the return value is the number of characters stored in the buffer
// pointed to by lpBuffer, not including the terminating null character.
//
// If lpBuffer is not large enough to hold the data, the return value is the buffer size, in
// characters, required to hold the string and its terminating null character and the contents of
// lpBuffer are undefined.
//
// If the function fails, the return value is zero. If the specified environment variable was not
// found in the environment block, GetLastError returns ERROR_ENVVAR_NOT_FOUND.
::SetLastError(ERROR_SUCCESS);
*valueLengthNeededWithNul = ::GetEnvironmentVariableW(key, value, static_cast<DWORD>(valueLength));
RETURN_LAST_ERROR_IF_EXPECTED((*valueLengthNeededWithNul == 0) && (::GetLastError() != ERROR_SUCCESS));
if (*valueLengthNeededWithNul < valueLength)
{
(*valueLengthNeededWithNul)++; // It fit, account for the null.
}
return S_OK;
});
}
/** Looks up the environment variable 'key' and returns null if it is not found.
'key' should not have '%' prefix and suffix. */
template <typename string_type>
HRESULT TryGetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
{
const auto hr = wil::GetEnvironmentVariableW<string_type>(key, result);
RETURN_HR_IF(hr, FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND)));
return S_OK;
}
/** Retrieves the fully qualified path for the file containing the specified module loaded
by a given process. Note GetModuleFileNameExW is a macro.*/
template <typename string_type, size_t initialBufferLength = 128>
HRESULT GetModuleFileNameExW(_In_opt_ HANDLE process, _In_opt_ HMODULE module, string_type& path)
{
// initialBufferLength is a template parameter to allow for testing. It creates some waste for
// shorter paths, but avoids iteration through the loop in common cases where paths are less
// than 128 characters.
// wil::max_extended_path_length + 1 (for the null char)
// + 1 (to be certain GetModuleFileNameExW didn't truncate)
size_t const ensureNoTrucation = (process != nullptr) ? 1 : 0;
size_t const maxExtendedPathLengthWithNull = wil::max_extended_path_length + 1 + ensureNoTrucation;
details::string_maker<string_type> maker;
for (size_t lengthWithNull = initialBufferLength;
lengthWithNull <= maxExtendedPathLengthWithNull;
lengthWithNull = (wistd::min)(lengthWithNull * 2, maxExtendedPathLengthWithNull))
{
// make() adds space for the trailing null
RETURN_IF_FAILED(maker.make(nullptr, lengthWithNull - 1));
DWORD copiedCount;
bool copyFailed;
bool copySucceededWithNoTruncation;
if (process != nullptr)
{
// GetModuleFileNameExW truncates and provides no error or other indication it has done so.
// The only way to be sure it didn't truncate is if it didn't need the whole buffer.
copiedCount = ::GetModuleFileNameExW(process, module, maker.buffer(), static_cast<DWORD>(lengthWithNull));
copyFailed = (0 == copiedCount);
copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull - 1);
}
else
{
// In cases of insufficient buffer, GetModuleFileNameW will return a value equal to lengthWithNull
// and set the last error to ERROR_INSUFFICIENT_BUFFER.
copiedCount = ::GetModuleFileNameW(module, maker.buffer(), static_cast<DWORD>(lengthWithNull));
copyFailed = (0 == copiedCount);
copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull);
}
if (copyFailed)
{
RETURN_LAST_ERROR();
}
else if (copySucceededWithNoTruncation)
{
path = maker.release();
return S_OK;
}
WI_ASSERT((process != nullptr) || (::GetLastError() == ERROR_INSUFFICIENT_BUFFER));
if (lengthWithNull == maxExtendedPathLengthWithNull)
{
// If we've reached this point, there's no point in trying a larger buffer size.
break;
}
}
// Any path should fit into the maximum max_extended_path_length. If we reached here, something went
// terribly wrong.
FAIL_FAST();
}
/** Retrieves the fully qualified path for the file that contains the specified module.
The module must have been loaded by the current process. The path returned will use the
same format that was specified when the module was loaded. Therefore, the path can be a
long or short file name, and can have the prefix '\\?\'. */
template <typename string_type, size_t initialBufferLength = 128>
HRESULT GetModuleFileNameW(HMODULE module, string_type& path)
{
return wil::GetModuleFileNameExW<string_type, initialBufferLength>(nullptr, module, path);
}
template <typename string_type, size_t stackBufferLength = 256>
HRESULT GetSystemDirectoryW(string_type& result) WI_NOEXCEPT
{
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
{
*valueLengthNeededWithNul = ::GetSystemDirectoryW(value, static_cast<DWORD>(valueLength));
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
if (*valueLengthNeededWithNul < valueLength)
{
(*valueLengthNeededWithNul)++; // it fit, account for the null
}
return S_OK;
});
}
#ifdef WIL_ENABLE_EXCEPTIONS
/** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
string_type ExpandEnvironmentStringsW(_In_ PCWSTR input)
{
string_type result;
THROW_IF_FAILED((wil::ExpandEnvironmentStringsW<string_type, stackBufferLength>(input, result)));
return result;
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
/** Searches for a specified file in a specified path using SearchPathW*/
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
string_type TrySearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, PCWSTR _In_opt_ extension)
{
string_type result;
HRESULT searchHR = wil::SearchPathW<string_type, stackBufferLength>(path, fileName, extension, result);
THROW_HR_IF(searchHR, FAILED(searchHR) && (searchHR != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)));
return result;
}
#endif
/** Looks up the environment variable 'key' and fails if it is not found.
'key' should not have '%' prefix and suffix.
Dangerous since environment variable generally are optional. */
template <typename string_type = wil::unique_cotaskmem_string>
string_type GetEnvironmentVariableW(_In_ PCWSTR key)
{
string_type result;
THROW_IF_FAILED(wil::GetEnvironmentVariableW<string_type>(key, result));
return result;
}
/** Looks up the environment variable 'key' and returns null if it is not found.
'key' should not have '%' prefix and suffix. */
template <typename string_type = wil::unique_cotaskmem_string>
string_type TryGetEnvironmentVariableW(_In_ PCWSTR key)
{
string_type result;
THROW_IF_FAILED(wil::TryGetEnvironmentVariableW<string_type>(key, result));
return result;
}
template <typename string_type = wil::unique_cotaskmem_string>
string_type GetModuleFileNameW(HMODULE module)
{
string_type result;
THROW_IF_FAILED(wil::GetModuleFileNameW(module, result));
return result;
}
template <typename string_type = wil::unique_cotaskmem_string>
string_type GetModuleFileNameExW(HANDLE process, HMODULE module)
{
string_type result;
THROW_IF_FAILED(wil::GetModuleFileNameExW(process, module, result));
return result;
}
#endif
/** Retrieve the HINSTANCE for the current DLL or EXE using this symbol that
the linker provides for every module. This avoids the need for a global HINSTANCE variable
and provides access to this value for static libraries. */
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
inline HINSTANCE GetModuleInstanceHandle() { return reinterpret_cast<HINSTANCE>(&__ImageBase); }
/// @cond
namespace details
{
class init_once_completer
{
INIT_ONCE& m_once;
unsigned long m_flags = INIT_ONCE_INIT_FAILED;
public:
init_once_completer(_In_ INIT_ONCE& once) : m_once(once)
{
}
#pragma warning(push)
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
void success()
{
m_flags = 0;
}
#pragma warning(pop)
~init_once_completer()
{
::InitOnceComplete(&m_once, m_flags, nullptr);
}
};
}
/// @endcond
/** Performs one-time initialization
Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked
at most once.
~~~~
INIT_ONCE g_init{};
ComPtr<IFoo> g_foo;
HRESULT MyMethod()
{
bool winner = false;
RETURN_IF_FAILED(wil::init_once_nothrow(g_init, []
{
ComPtr<IFoo> foo;
RETURN_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo));
RETURN_IF_FAILED(foo->Startup());
g_foo = foo;
}, &winner);
if (winner)
{
RETURN_IF_FAILED(g_foo->Another());
}
return S_OK;
}
~~~~
See MSDN for more information on `InitOnceExecuteOnce`.
@param initOnce The INIT_ONCE structure to use as context for initialization.
@param func A function that will be invoked to perform initialization. If this fails, the init call
fails and the once-init is not marked as initialized. A later caller could attempt to
initialize it a second time.
@param callerCompleted Set to 'true' if this was the call that caused initialization, false otherwise.
*/
template<typename T> HRESULT init_once_nothrow(_Inout_ INIT_ONCE& initOnce, T func, _Out_opt_ bool* callerCompleted = nullptr) WI_NOEXCEPT
{
BOOL pending = FALSE;
wil::assign_to_opt_param(callerCompleted, false);
__WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr));
if (pending)
{
details::init_once_completer completion(initOnce);
__WIL_PRIVATE_RETURN_IF_FAILED(func());
completion.success();
wil::assign_to_opt_param(callerCompleted, true);
}
return S_OK;
}
//! Similar to init_once_nothrow, but fails-fast if the initialization step failed. The 'callerComplete' value is
//! returned to the caller instead of being an out-parameter.
template<typename T> bool init_once_failfast(_Inout_ INIT_ONCE& initOnce, T&& func) WI_NOEXCEPT
{
bool callerCompleted;
FAIL_FAST_IF_FAILED(init_once_nothrow(initOnce, wistd::forward<T>(func), &callerCompleted));
return callerCompleted;
};
//! Returns 'true' if this `init_once` structure has finished initialization, false otherwise.
inline bool init_once_initialized(_Inout_ INIT_ONCE& initOnce) WI_NOEXCEPT
{
BOOL pending = FALSE;
return ::InitOnceBeginInitialize(&initOnce, INIT_ONCE_CHECK_ONLY, &pending, nullptr) && !pending;
}
#ifdef WIL_ENABLE_EXCEPTIONS
/** Performs one-time initialization
Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked
at most once.
~~~~
INIT_ONCE g_init{};
ComPtr<IFoo> g_foo;
void MyMethod()
{
bool winner = wil::init_once(g_init, []
{
ComPtr<IFoo> foo;
THROW_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo));
THROW_IF_FAILED(foo->Startup());
g_foo = foo;
});
if (winner)
{
THROW_IF_FAILED(g_foo->Another());
}
}
~~~~
See MSDN for more information on `InitOnceExecuteOnce`.
@param initOnce The INIT_ONCE structure to use as context for initialization.
@param func A function that will be invoked to perform initialization. If this fails, the init call
fails and the once-init is not marked as initialized. A later caller could attempt to
initialize it a second time.
@returns 'true' if this was the call that caused initialization, false otherwise.
*/
template<typename T> bool init_once(_Inout_ INIT_ONCE& initOnce, T func)
{
BOOL pending = FALSE;
THROW_IF_WIN32_BOOL_FALSE(::InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr));
if (pending)
{
details::init_once_completer completion(initOnce);
func();
completion.success();
return true;
}
else
{
return false;
}
}
#endif // WIL_ENABLE_EXCEPTIONS
}
// Macro for calling GetProcAddress(), with type safety for C++ clients
// using the type information from the specified function.
// The return value is automatically cast to match the function prototype of the input function.
//
// Sample usage:
//
// auto sendMail = GetProcAddressByFunctionDeclaration(hinstMAPI, MAPISendMailW);
// if (sendMail)
// {
// sendMail(0, 0, pmm, MAPI_USE_DEFAULT, 0);
// }
// Declaration
#define GetProcAddressByFunctionDeclaration(hinst, fn) reinterpret_cast<decltype(::fn)*>(GetProcAddress(hinst, #fn))
#endif // __WIL_WIN32_HELPERS_INCLUDED

2231
Externals/WIL/include/wil/winrt.h vendored Normal file

File diff suppressed because it is too large Load diff

548
Externals/WIL/include/wil/wistd_config.h vendored Normal file
View file

@ -0,0 +1,548 @@
// -*- C++ -*-
//===--------------------------- __config ---------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// STL common functionality
//
// Some aspects of STL are core language concepts that should be used from all C++ code, regardless
// of whether exceptions are enabled in the component. Common library code that expects to be used
// from exception-free components want these concepts, but including STL headers directly introduces
// friction as it requires components not using STL to declare their STL version. Doing so creates
// ambiguity around whether STL use is safe in a particular component and implicitly brings in
// a long list of headers (including <new>) which can create further ambiguity around throwing new
// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has
// the potential to create naming conflicts or other implied dependencies.
//
// To promote the use of these core language concepts outside of STL-based binaries, this file is
// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding
// "std::" namespace STL functions and types should be preferred over these in code that is bound to
// STL. The implementation and naming of all functions are taken directly from STL, instead using
// "wistd" (Windows Implementation std) as the namespace.
//
// Routines in this namespace should always be considered a reflection of the *current* STL implementation
// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here.
//
// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation.
// Only code that is not exception-based and libraries that expect to be utilized across both exception
// and non-exception based code should utilize this functionality.
// This header mimics libc++'s '__config' header to the extent necessary to get the wistd::* definitions compiling. Note
// that this has a few key differences since libc++'s MSVC compatability is currently not functional and a bit behind
#ifndef _WISTD_CONFIG_H_
#define _WISTD_CONFIG_H_
// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage
#include <stddef.h> // For size_t and other necessary types
/// @cond
#if defined(_MSC_VER) && !defined(__clang__)
# if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# define __WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER
# endif
#endif
#ifndef __WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER
#pragma GCC system_header
#endif
#ifdef __GNUC__
# define __WI_GNUC_VER (__GNUC__ * 100 + __GNUC_MINOR__)
// The __WI_GNUC_VER_NEW macro better represents the new GCC versioning scheme
// introduced in GCC 5.0.
# define __WI_GNUC_VER_NEW (__WI_GNUC_VER * 10 + __GNUC_PATCHLEVEL__)
#else
# define __WI_GNUC_VER 0
# define __WI_GNUC_VER_NEW 0
#endif
// _MSVC_LANG is the more accurate way to get the C++ version in MSVC
#if defined(_MSVC_LANG) && (_MSVC_LANG > __cplusplus)
#define __WI_CPLUSPLUS _MSVC_LANG
#else
#define __WI_CPLUSPLUS __cplusplus
#endif
#ifndef __WI_LIBCPP_STD_VER
# if __WI_CPLUSPLUS <= 201103L
# define __WI_LIBCPP_STD_VER 11
# elif __WI_CPLUSPLUS <= 201402L
# define __WI_LIBCPP_STD_VER 14
# elif __WI_CPLUSPLUS <= 201703L
# define __WI_LIBCPP_STD_VER 17
# else
# define __WI_LIBCPP_STD_VER 18 // current year, or date of c++2a ratification
# endif
#endif // __WI_LIBCPP_STD_VER
#if __WI_CPLUSPLUS < 201103L
#define __WI_LIBCPP_CXX03_LANG
#endif
#if defined(__ELF__)
# define __WI_LIBCPP_OBJECT_FORMAT_ELF 1
#elif defined(__MACH__)
# define __WI_LIBCPP_OBJECT_FORMAT_MACHO 1
#elif defined(_WIN32)
# define __WI_LIBCPP_OBJECT_FORMAT_COFF 1
#elif defined(__wasm__)
# define __WI_LIBCPP_OBJECT_FORMAT_WASM 1
#else
# error Unknown object file format
#endif
#if defined(__clang__)
# define __WI_LIBCPP_COMPILER_CLANG
#elif defined(__GNUC__)
# define __WI_LIBCPP_COMPILER_GCC
#elif defined(_MSC_VER)
# define __WI_LIBCPP_COMPILER_MSVC
#elif defined(__IBMCPP__)
# define __WI_LIBCPP_COMPILER_IBM
#endif
// NOTE: MSVC, which is what we primarily target, is severly underrepresented in libc++ and checks such as
// __has_feature(...) are always false for MSVC, even when the feature being tested _is_ present in MSVC. Therefore, we
// instead modify all checks to be __WI_HAS_FEATURE_IS_UNION, etc., which provides the correct value for MSVC and falls
// back to the __has_feature(...), etc. value otherwise. We intentionally leave '__has_feature', etc. undefined for MSVC
// so that we don't accidentally use the incorrect behavior
#ifndef __WI_LIBCPP_COMPILER_MSVC
#ifndef __has_feature
#define __has_feature(__x) 0
#endif
// '__is_identifier' returns '0' if '__x' is a reserved identifier provided by
// the compiler and '1' otherwise.
#ifndef __is_identifier
#define __is_identifier(__x) 1
#endif
#ifndef __has_cpp_attribute
#define __has_cpp_attribute(__x) 0
#endif
#ifndef __has_attribute
#define __has_attribute(__x) 0
#endif
#ifndef __has_builtin
#define __has_builtin(__x) 0
#endif
#if __has_feature(cxx_alignas)
# define __WI_ALIGNAS_TYPE(x) alignas(x)
# define __WI_ALIGNAS(x) alignas(x)
#else
# define __WI_ALIGNAS_TYPE(x) __attribute__((__aligned__(__alignof(x))))
# define __WI_ALIGNAS(x) __attribute__((__aligned__(x)))
#endif
#if __has_feature(cxx_explicit_conversions) || defined(__IBMCPP__) || \
(!defined(__WI_LIBCPP_CXX03_LANG) && defined(__GNUC__)) // All supported GCC versions
# define __WI_LIBCPP_EXPLICIT explicit
#else
# define __WI_LIBCPP_EXPLICIT
#endif
#if __has_feature(cxx_attributes)
# define __WI_LIBCPP_NORETURN [[noreturn]]
#else
# define __WI_LIBCPP_NORETURN __attribute__ ((noreturn))
#endif
#define __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
#define __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS
// The __WI_LIBCPP_NODISCARD_ATTRIBUTE should only be used to define other
// NODISCARD macros to the correct attribute.
#if __has_cpp_attribute(nodiscard)
# define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[nodiscard]]
#elif defined(__WI_LIBCPP_COMPILER_CLANG) && !defined(__WI_LIBCPP_CXX03_LANG)
# define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[clang::warn_unused_result]]
#else
// We can't use GCC's [[gnu::warn_unused_result]] and
// __attribute__((warn_unused_result)), because GCC does not silence them via
// (void) cast.
# define __WI_LIBCPP_NODISCARD_ATTRIBUTE
#endif
#define __WI_HAS_FEATURE_IS_UNION __has_feature(is_union)
#define __WI_HAS_FEATURE_IS_CLASS __has_feature(is_class)
#define __WI_HAS_FEATURE_IS_ENUM __has_feature(is_enum)
#define __WI_HAS_FEATURE_IS_CONVERTIBLE_TO __has_feature(is_convertible_to)
#define __WI_HAS_FEATURE_IS_EMPTY __has_feature(is_empty)
#define __WI_HAS_FEATURE_IS_POLYMORPHIC __has_feature(is_polymorphic)
#define __WI_HAS_FEATURE_HAS_VIRTUAL_DESTRUCTOR __has_feature(has_virtual_destructor)
#define __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS __has_feature(cxx_reference_qualified_functions)
#define __WI_HAS_FEATURE_IS_CONSTRUCTIBLE __has_feature(is_constructible)
#define __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE __has_feature(is_trivially_constructible)
#define __WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE __has_feature(is_trivially_assignable)
#define __WI_HAS_FEATURE_HAS_TRIVIAL_DESTRUCTOR __has_feature(has_trivial_destructor)
#define __WI_HAS_FEATURE_NOEXCEPT __has_feature(cxx_noexcept)
#define __WI_HAS_FEATURE_IS_POD __has_feature(is_pod)
#define __WI_HAS_FEATURE_IS_STANDARD_LAYOUT __has_feature(is_standard_layout)
#define __WI_HAS_FEATURE_IS_TRIVIALLY_COPYABLE __has_feature(is_trivially_copyable)
#define __WI_HAS_FEATURE_IS_TRIVIAL __has_feature(is_trivial)
#define __WI_HAS_FEATURE_HAS_TRIVIAL_CONSTRUCTOR __has_feature(has_trivial_constructor) || (__WI_GNUC_VER >= 403)
#define __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR __has_feature(has_nothrow_constructor) || (__WI_GNUC_VER >= 403)
#define __WI_HAS_FEATURE_HAS_NOTHROW_COPY __has_feature(has_nothrow_copy) || (__WI_GNUC_VER >= 403)
#define __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN __has_feature(has_nothrow_assign) || (__WI_GNUC_VER >= 403)
#if !(__has_feature(cxx_noexcept))
#define __WI_LIBCPP_HAS_NO_NOEXCEPT
#endif
#if !__is_identifier(__has_unique_object_representations) || __WI_GNUC_VER >= 700
#define __WI_LIBCPP_HAS_UNIQUE_OBJECT_REPRESENTATIONS
#endif
#if !(__has_feature(cxx_variadic_templates))
#define __WI_LIBCPP_HAS_NO_VARIADICS
#endif
#if __has_feature(is_literal) || __WI_GNUC_VER >= 407
#define __WI_LIBCPP_IS_LITERAL(T) __is_literal(T)
#endif
#if __has_feature(underlying_type) || __WI_GNUC_VER >= 407
#define __WI_LIBCPP_UNDERLYING_TYPE(T) __underlying_type(T)
#endif
#if __has_feature(is_final) || __WI_GNUC_VER >= 407
#define __WI_LIBCPP_HAS_IS_FINAL
#endif
#if __has_feature(is_base_of) || defined(__GNUC__) && __WI_GNUC_VER >= 403
#define __WI_LIBCPP_HAS_IS_BASE_OF
#endif
#if __is_identifier(__is_aggregate) && (__WI_GNUC_VER_NEW < 7001)
#define __WI_LIBCPP_HAS_NO_IS_AGGREGATE
#endif
#if !(__has_feature(cxx_rtti)) && !defined(__WI_LIBCPP_NO_RTTI)
#define __WI_LIBCPP_NO_RTTI
#endif
#if !(__has_feature(cxx_variable_templates))
#define __WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES
#endif
#if !(__has_feature(cxx_relaxed_constexpr))
#define __WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR
#endif
#if !__has_builtin(__builtin_addressof) && _GNUC_VER < 700
#define __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF
#endif
#if __has_attribute(__no_sanitize__) && !defined(__WI_LIBCPP_COMPILER_GCC)
# define __WI_LIBCPP_NO_CFI __attribute__((__no_sanitize__("cfi")))
#else
# define __WI_LIBCPP_NO_CFI
#endif
#define __WI_LIBCPP_ALWAYS_INLINE __attribute__ ((__always_inline__))
#if __has_attribute(internal_linkage)
# define __WI_LIBCPP_INTERNAL_LINKAGE __attribute__ ((internal_linkage))
#else
# define __WI_LIBCPP_INTERNAL_LINKAGE __WI_LIBCPP_ALWAYS_INLINE
#endif
#else
// NOTE: Much of the following assumes a decently recent version of MSVC. Past versions can be supported, but will need
// to be updated to contain the proper _MSC_VER check
#define __WI_ALIGNAS_TYPE(x) alignas(x)
#define __WI_ALIGNAS(x) alignas(x)
#define __alignof__ __alignof
#define __WI_LIBCPP_EXPLICIT explicit
#define __WI_LIBCPP_NORETURN [[noreturn]]
#define __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __pragma(warning(suppress:26495))
#define __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS __pragma(warning(suppress:26439))
#if __WI_LIBCPP_STD_VER > 14
#define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[nodiscard]]
#else
#define __WI_LIBCPP_NODISCARD_ATTRIBUTE _Check_return_
#endif
#define __WI_HAS_FEATURE_IS_UNION 1
#define __WI_HAS_FEATURE_IS_CLASS 1
#define __WI_HAS_FEATURE_IS_ENUM 1
#define __WI_HAS_FEATURE_IS_CONVERTIBLE_TO 1
#define __WI_HAS_FEATURE_IS_EMPTY 1
#define __WI_HAS_FEATURE_IS_POLYMORPHIC 1
#define __WI_HAS_FEATURE_HAS_VIRTUAL_DESTRUCTOR 1
#define __WI_LIBCPP_HAS_UNIQUE_OBJECT_REPRESENTATIONS 1
#define __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS 1
#define __WI_HAS_FEATURE_IS_CONSTRUCTIBLE 1
#define __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE 1
#define __WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE 1
#define __WI_HAS_FEATURE_HAS_TRIVIAL_DESTRUCTOR 1
#define __WI_HAS_FEATURE_NOEXCEPT 1
#define __WI_HAS_FEATURE_IS_POD 1
#define __WI_HAS_FEATURE_IS_STANDARD_LAYOUT 1
#define __WI_HAS_FEATURE_IS_TRIVIALLY_COPYABLE 1
#define __WI_HAS_FEATURE_IS_TRIVIAL 1
#define __WI_HAS_FEATURE_HAS_TRIVIAL_CONSTRUCTOR 1
#define __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR 1
#define __WI_HAS_FEATURE_HAS_NOTHROW_COPY 1
#define __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN 1
#define __WI_HAS_FEATURE_IS_DESTRUCTIBLE 1
#if !defined(_CPPRTTI) && !defined(__WI_LIBCPP_NO_RTTI)
#define __WI_LIBCPP_NO_RTTI
#endif
#define __WI_LIBCPP_IS_LITERAL(T) __is_literal_type(T)
#define __WI_LIBCPP_UNDERLYING_TYPE(T) __underlying_type(T)
#define __WI_LIBCPP_HAS_IS_FINAL
#define __WI_LIBCPP_HAS_IS_BASE_OF
#if __WI_LIBCPP_STD_VER < 14
#define __WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES
#endif
#define __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF
#define __WI_LIBCPP_NO_CFI
#define __WI_LIBCPP_ALWAYS_INLINE __forceinline
#define __WI_LIBCPP_INTERNAL_LINKAGE
#endif
#ifndef _WIN32
#ifdef __LITTLE_ENDIAN__
# if __LITTLE_ENDIAN__
# define __WI_LIBCPP_LITTLE_ENDIAN
# endif // __LITTLE_ENDIAN__
#endif // __LITTLE_ENDIAN__
#ifdef __BIG_ENDIAN__
# if __BIG_ENDIAN__
# define __WI_LIBCPP_BIG_ENDIAN
# endif // __BIG_ENDIAN__
#endif // __BIG_ENDIAN__
#ifdef __BYTE_ORDER__
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define __WI_LIBCPP_LITTLE_ENDIAN
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define __WI_LIBCPP_BIG_ENDIAN
# endif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
#endif // __BYTE_ORDER__
#if !defined(__WI_LIBCPP_LITTLE_ENDIAN) && !defined(__WI_LIBCPP_BIG_ENDIAN)
# include <endian.h>
# if __BYTE_ORDER == __LITTLE_ENDIAN
# define __WI_LIBCPP_LITTLE_ENDIAN
# elif __BYTE_ORDER == __BIG_ENDIAN
# define __WI_LIBCPP_BIG_ENDIAN
# else // __BYTE_ORDER == __BIG_ENDIAN
# error unable to determine endian
# endif
#endif // !defined(__WI_LIBCPP_LITTLE_ENDIAN) && !defined(__WI_LIBCPP_BIG_ENDIAN)
#else // _WIN32
#define __WI_LIBCPP_LITTLE_ENDIAN
#endif // _WIN32
#ifdef __WI_LIBCPP_HAS_NO_CONSTEXPR
# define __WI_LIBCPP_CONSTEXPR
#else
# define __WI_LIBCPP_CONSTEXPR constexpr
#endif
#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 constexpr
#else
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
#endif
#if __WI_LIBCPP_STD_VER > 14 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX14 constexpr
#else
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX14
#endif
#if __WI_LIBCPP_STD_VER > 17 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX17 constexpr
#else
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX17
#endif
#if !defined(__WI_LIBCPP_DISABLE_NODISCARD_AFTER_CXX17) && \
(__WI_LIBCPP_STD_VER > 17 || defined(__WI_LIBCPP_ENABLE_NODISCARD))
# define __WI_LIBCPP_NODISCARD_AFTER_CXX17 __WI_LIBCPP_NODISCARD_ATTRIBUTE
#else
# define __WI_LIBCPP_NODISCARD_AFTER_CXX17
#endif
#if __WI_LIBCPP_STD_VER > 14 && defined(__cpp_inline_variables) && (__cpp_inline_variables >= 201606L)
# define __WI_LIBCPP_INLINE_VAR inline
#else
# define __WI_LIBCPP_INLINE_VAR
#endif
#ifdef __WI_LIBCPP_CXX03_LANG
#define __WI_LIBCPP_HAS_NO_UNICODE_CHARS
#define __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES
#endif
#ifndef __SIZEOF_INT128__
#define __WI_LIBCPP_HAS_NO_INT128
#endif
#if !__WI_HAS_FEATURE_NOEXCEPT && !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT)
#define __WI_LIBCPP_HAS_NO_NOEXCEPT
#endif
#ifndef __WI_LIBCPP_HAS_NO_NOEXCEPT
# define WI_NOEXCEPT noexcept
# define __WI_NOEXCEPT_(x) noexcept(x)
#else
# define WI_NOEXCEPT throw()
# define __WI_NOEXCEPT_(x)
#endif
#if defined(__WI_LIBCPP_OBJECT_FORMAT_COFF)
#define __WI_LIBCPP_HIDDEN
#define __WI_LIBCPP_TEMPLATE_VIS
#endif // defined(__WI_LIBCPP_OBJECT_FORMAT_COFF)
#ifndef __WI_LIBCPP_HIDDEN
# if !defined(__WI_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS)
# define __WI_LIBCPP_HIDDEN __attribute__ ((__visibility__("hidden")))
# else
# define __WI_LIBCPP_HIDDEN
# endif
#endif
#ifndef __WI_LIBCPP_TEMPLATE_VIS
# if !defined(__WI_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS) && !defined(__WI_LIBCPP_COMPILER_MSVC)
# if __has_attribute(__type_visibility__)
# define __WI_LIBCPP_TEMPLATE_VIS __attribute__ ((__type_visibility__("default")))
# else
# define __WI_LIBCPP_TEMPLATE_VIS __attribute__ ((__visibility__("default")))
# endif
# else
# define __WI_LIBCPP_TEMPLATE_VIS
# endif
#endif
#define __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_HIDDEN __WI_LIBCPP_INTERNAL_LINKAGE
namespace wistd // ("Windows Implementation" std)
{
typedef decltype(__nullptr) nullptr_t;
template <class _T1, class _T2 = _T1>
struct __less
{
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T1& __x, const _T2& __y) const {return __x < __y;}
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T2& __x, const _T1& __y) const {return __x < __y;}
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T2& __x, const _T2& __y) const {return __x < __y;}
};
template <class _T1>
struct __less<_T1, _T1>
{
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
};
template <class _T1>
struct __less<const _T1, _T1>
{
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
};
template <class _T1>
struct __less<_T1, const _T1>
{
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
};
// These are added to wistd to enable use of min/max without having to use the windows.h min/max
// macros that some clients might not have access to. Note: the STL versions of these have debug
// checking for the less than operator and support for iterators that these implementations lack.
// Use the STL versions when you require use of those features.
// min
template <class _Tp, class _Compare>
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
const _Tp&
(min)(const _Tp& __a, const _Tp& __b, _Compare __comp)
{
return __comp(__b, __a) ? __b : __a;
}
template <class _Tp>
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
const _Tp&
(min)(const _Tp& __a, const _Tp& __b)
{
return (min)(__a, __b, __less<_Tp>());
}
// max
template <class _Tp, class _Compare>
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
const _Tp&
(max)(const _Tp& __a, const _Tp& __b, _Compare __comp)
{
return __comp(__a, __b) ? __b : __a;
}
template <class _Tp>
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
const _Tp&
(max)(const _Tp& __a, const _Tp& __b)
{
return (max)(__a, __b, __less<_Tp>());
}
template <class _Arg, class _Result>
struct __WI_LIBCPP_TEMPLATE_VIS unary_function
{
typedef _Arg argument_type;
typedef _Result result_type;
};
template <class _Arg1, class _Arg2, class _Result>
struct __WI_LIBCPP_TEMPLATE_VIS binary_function
{
typedef _Arg1 first_argument_type;
typedef _Arg2 second_argument_type;
typedef _Result result_type;
};
}
/// @endcond
#endif _WISTD_CONFIG_H_

View file

@ -0,0 +1,543 @@
// -*- C++ -*-
//===------------------------ functional ----------------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
// STL common functionality
//
// Some aspects of STL are core language concepts that should be used from all C++ code, regardless
// of whether exceptions are enabled in the component. Common library code that expects to be used
// from exception-free components want these concepts, but including STL headers directly introduces
// friction as it requires components not using STL to declare their STL version. Doing so creates
// ambiguity around whether STL use is safe in a particular component and implicitly brings in
// a long list of headers (including <new>) which can create further ambiguity around throwing new
// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has
// the potential to create naming conflicts or other implied dependencies.
//
// To promote the use of these core language concepts outside of STL-based binaries, this file is
// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding
// "std::" namespace STL functions and types should be preferred over these in code that is bound to
// STL. The implementation and naming of all functions are taken directly from STL, instead using
// "wistd" (Windows Implementation std) as the namespace.
//
// Routines in this namespace should always be considered a reflection of the *current* STL implementation
// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here.
//
// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation.
// Only code that is not exception-based and libraries that expect to be utilized across both exception
// and non-exception based code should utilize this functionality.
#ifndef _WISTD_FUNCTIONAL_H_
#define _WISTD_FUNCTIONAL_H_
// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage
#include "wistd_memory.h"
#include <intrin.h> // For __fastfail
#include <new.h> // For placement new
#if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
#pragma GCC system_header
#endif
#pragma warning(push)
#pragma warning(disable: 4324)
/// @cond
namespace wistd // ("Windows Implementation" std)
{
// wistd::function
//
// All of the code below is in direct support of wistd::function. This class is identical to std::function
// with the following exceptions:
//
// 1) It never allocates and is safe to use from exception-free code (custom allocators are not supported)
// 2) It's slightly bigger on the stack (64 bytes, rather than 24 for 32bit)
// 3) There is an explicit static-assert if a lambda becomes too large to hold in the internal buffer (rather than an allocation)
template <class _Ret>
struct __invoke_void_return_wrapper
{
#ifndef __WI_LIBCPP_CXX03_LANG
template <class ..._Args>
static _Ret __call(_Args&&... __args) {
return __invoke(wistd::forward<_Args>(__args)...);
}
#else
template <class _Fn>
static _Ret __call(_Fn __f) {
return __invoke(__f);
}
template <class _Fn, class _A0>
static _Ret __call(_Fn __f, _A0& __a0) {
return __invoke(__f, __a0);
}
template <class _Fn, class _A0, class _A1>
static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1) {
return __invoke(__f, __a0, __a1);
}
template <class _Fn, class _A0, class _A1, class _A2>
static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2){
return __invoke(__f, __a0, __a1, __a2);
}
#endif
};
template <>
struct __invoke_void_return_wrapper<void>
{
#ifndef __WI_LIBCPP_CXX03_LANG
template <class ..._Args>
static void __call(_Args&&... __args) {
(void)__invoke(wistd::forward<_Args>(__args)...);
}
#else
template <class _Fn>
static void __call(_Fn __f) {
__invoke(__f);
}
template <class _Fn, class _A0>
static void __call(_Fn __f, _A0& __a0) {
__invoke(__f, __a0);
}
template <class _Fn, class _A0, class _A1>
static void __call(_Fn __f, _A0& __a0, _A1& __a1) {
__invoke(__f, __a0, __a1);
}
template <class _Fn, class _A0, class _A1, class _A2>
static void __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2) {
__invoke(__f, __a0, __a1, __a2);
}
#endif
};
////////////////////////////////////////////////////////////////////////////////
// FUNCTION
//==============================================================================
// bad_function_call
__WI_LIBCPP_NORETURN inline __WI_LIBCPP_INLINE_VISIBILITY
void __throw_bad_function_call()
{
__fastfail(7); // FAST_FAIL_FATAL_APP_EXIT
}
template<class _Fp> class __WI_LIBCPP_TEMPLATE_VIS function; // undefined
namespace __function
{
template<class _Rp>
struct __maybe_derive_from_unary_function
{
};
template<class _Rp, class _A1>
struct __maybe_derive_from_unary_function<_Rp(_A1)>
: public unary_function<_A1, _Rp>
{
};
template<class _Rp>
struct __maybe_derive_from_binary_function
{
};
template<class _Rp, class _A1, class _A2>
struct __maybe_derive_from_binary_function<_Rp(_A1, _A2)>
: public binary_function<_A1, _A2, _Rp>
{
};
template <class _Fp>
__WI_LIBCPP_INLINE_VISIBILITY
bool __not_null(_Fp const&) { return true; }
template <class _Fp>
__WI_LIBCPP_INLINE_VISIBILITY
bool __not_null(_Fp* __ptr) { return __ptr; }
template <class _Ret, class _Class>
__WI_LIBCPP_INLINE_VISIBILITY
bool __not_null(_Ret _Class::*__ptr) { return __ptr; }
template <class _Fp>
__WI_LIBCPP_INLINE_VISIBILITY
bool __not_null(function<_Fp> const& __f) { return !!__f; }
} // namespace __function
#ifndef __WI_LIBCPP_CXX03_LANG
namespace __function {
template<class _Fp> class __base;
template<class _Rp, class ..._ArgTypes>
class __base<_Rp(_ArgTypes...)>
{
__base(const __base&);
__base& operator=(const __base&);
public:
__WI_LIBCPP_INLINE_VISIBILITY __base() {}
__WI_LIBCPP_INLINE_VISIBILITY virtual ~__base() {}
virtual void __clone(__base*) const = 0;
virtual void __move(__base*) = 0;
virtual void destroy() WI_NOEXCEPT = 0;
virtual _Rp operator()(_ArgTypes&& ...) = 0;
};
template<class _FD, class _FB> class __func;
template<class _Fp, class _Rp, class ..._ArgTypes>
class __func<_Fp, _Rp(_ArgTypes...)>
: public __base<_Rp(_ArgTypes...)>
{
_Fp __f_;
public:
__WI_LIBCPP_INLINE_VISIBILITY
explicit __func(_Fp&& __f)
: __f_(wistd::move(__f)) {}
__WI_LIBCPP_INLINE_VISIBILITY
explicit __func(const _Fp& __f)
: __f_(__f) {}
virtual void __clone(__base<_Rp(_ArgTypes...)>*) const;
virtual void __move(__base<_Rp(_ArgTypes...)>*);
virtual void destroy() WI_NOEXCEPT;
virtual _Rp operator()(_ArgTypes&& ... __arg);
};
template<class _Fp, class _Rp, class ..._ArgTypes>
void
__func<_Fp, _Rp(_ArgTypes...)>::__clone(__base<_Rp(_ArgTypes...)>* __p) const
{
::new (__p) __func(__f_);
}
template<class _Fp, class _Rp, class ..._ArgTypes>
void
__func<_Fp, _Rp(_ArgTypes...)>::__move(__base<_Rp(_ArgTypes...)>* __p)
{
::new (__p) __func(wistd::move(__f_));
}
template<class _Fp, class _Rp, class ..._ArgTypes>
void
__func<_Fp, _Rp(_ArgTypes...)>::destroy() WI_NOEXCEPT
{
__f_.~_Fp();
}
template<class _Fp, class _Rp, class ..._ArgTypes>
_Rp
__func<_Fp, _Rp(_ArgTypes...)>::operator()(_ArgTypes&& ... __arg)
{
typedef __invoke_void_return_wrapper<_Rp> _Invoker;
return _Invoker::__call(__f_, wistd::forward<_ArgTypes>(__arg)...);
}
} // __function
template<class _Rp, class ..._ArgTypes>
class __WI_LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)>
: public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>,
public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)>
{
// 'wistd::function' is most similar to 'inplace_function' in that it _only_ permits holding function objects
// that can fit within its internal buffer. Therefore, we expand this size to accommodate space for at least 12
// pointers (__base vtable takes an additional one).
static constexpr size_t __buffer_size = 13 * sizeof(void*);
typedef __function::__base<_Rp(_ArgTypes...)> __base;
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
typename aligned_storage<__buffer_size>::type __buf_;
__base* __f_;
__WI_LIBCPP_NO_CFI static __base *__as_base(void *p) {
return reinterpret_cast<__base*>(p);
}
template <class _Fp, bool>
struct __callable_imp
{
static const bool value = is_same<void, _Rp>::value ||
is_convertible<typename __invoke_of<_Fp&, _ArgTypes...>::type,
_Rp>::value;
};
template <class _Fp>
struct __callable_imp<_Fp, false>
{
static const bool value = false;
};
template <class _Fp>
struct __callable
{
static const bool value = __callable_imp<_Fp, __lazy_and<
integral_constant<bool, !is_same<__uncvref_t<_Fp>, function>::value>,
__invokable<_Fp&, _ArgTypes...>
>::value>::value;
};
template <class _Fp>
using _EnableIfCallable = typename enable_if<__callable<_Fp>::value>::type;
public:
typedef _Rp result_type;
// construct/copy/destroy:
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
function() WI_NOEXCEPT : __f_(0) {}
__WI_LIBCPP_INLINE_VISIBILITY
function(nullptr_t) WI_NOEXCEPT : __f_(0) {}
function(const function&);
function(function&&);
template<class _Fp, class = _EnableIfCallable<_Fp>>
function(_Fp);
function& operator=(const function&);
function& operator=(function&&);
function& operator=(nullptr_t) WI_NOEXCEPT;
template<class _Fp, class = _EnableIfCallable<_Fp>>
function& operator=(_Fp&&);
~function();
// function modifiers:
void swap(function&);
// function capacity:
__WI_LIBCPP_INLINE_VISIBILITY
__WI_LIBCPP_EXPLICIT operator bool() const WI_NOEXCEPT {return __f_;}
// deleted overloads close possible hole in the type system
template<class _R2, class... _ArgTypes2>
bool operator==(const function<_R2(_ArgTypes2...)>&) const = delete;
template<class _R2, class... _ArgTypes2>
bool operator!=(const function<_R2(_ArgTypes2...)>&) const = delete;
public:
// function invocation:
_Rp operator()(_ArgTypes...) const;
// NOTE: type_info is very compiler specific, and on top of that, we're operating in a namespace other than
// 'std' so all functions requiring RTTI have been removed
};
template<class _Rp, class ..._ArgTypes>
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
function<_Rp(_ArgTypes...)>::function(const function& __f)
{
if (__f.__f_ == 0)
__f_ = 0;
else
{
__f_ = __as_base(&__buf_);
__f.__f_->__clone(__f_);
}
}
template<class _Rp, class ..._ArgTypes>
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS
function<_Rp(_ArgTypes...)>::function(function&& __f)
{
if (__f.__f_ == 0)
__f_ = 0;
else
{
__f_ = __as_base(&__buf_);
__f.__f_->__move(__f_);
__f.__f_->destroy();
__f.__f_ = 0;
}
}
template<class _Rp, class ..._ArgTypes>
template <class _Fp, class>
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
function<_Rp(_ArgTypes...)>::function(_Fp __f)
: __f_(0)
{
if (__function::__not_null(__f))
{
typedef __function::__func<_Fp, _Rp(_ArgTypes...)> _FF;
static_assert(sizeof(_FF) <= sizeof(__buf_),
"The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture.");
__f_ = ::new((void*)&__buf_) _FF(wistd::move(__f));
}
}
template<class _Rp, class ..._ArgTypes>
function<_Rp(_ArgTypes...)>&
function<_Rp(_ArgTypes...)>::operator=(const function& __f)
{
*this = nullptr;
if (__f.__f_)
{
__f_ = __as_base(&__buf_);
__f.__f_->__clone(__f_);
}
return *this;
}
template<class _Rp, class ..._ArgTypes>
function<_Rp(_ArgTypes...)>&
function<_Rp(_ArgTypes...)>::operator=(function&& __f)
{
*this = nullptr;
if (__f.__f_)
{
__f_ = __as_base(&__buf_);
__f.__f_->__move(__f_);
__f.__f_->destroy();
__f.__f_ = 0;
}
return *this;
}
template<class _Rp, class ..._ArgTypes>
function<_Rp(_ArgTypes...)>&
function<_Rp(_ArgTypes...)>::operator=(nullptr_t) WI_NOEXCEPT
{
__base* __t = __f_;
__f_ = 0;
if (__t)
__t->destroy();
return *this;
}
template<class _Rp, class ..._ArgTypes>
template <class _Fp, class>
function<_Rp(_ArgTypes...)>&
function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f)
{
*this = nullptr;
if (__function::__not_null(__f))
{
typedef __function::__func<typename decay<_Fp>::type, _Rp(_ArgTypes...)> _FF;
static_assert(sizeof(_FF) <= sizeof(__buf_),
"The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture.");
__f_ = ::new((void*)&__buf_) _FF(wistd::move(__f));
}
return *this;
}
template<class _Rp, class ..._ArgTypes>
function<_Rp(_ArgTypes...)>::~function()
{
if (__f_)
__f_->destroy();
}
template<class _Rp, class ..._ArgTypes>
void
function<_Rp(_ArgTypes...)>::swap(function& __f)
{
if (wistd::addressof(__f) == this)
return;
if (__f_ && __f.__f_)
{
typename aligned_storage<sizeof(__buf_)>::type __tempbuf;
__base* __t = __as_base(&__tempbuf);
__f_->__move(__t);
__f_->destroy();
__f_ = 0;
__f.__f_->__move(__as_base(&__buf_));
__f.__f_->destroy();
__f.__f_ = 0;
__f_ = __as_base(&__buf_);
__t->__move(__as_base(&__f.__buf_));
__t->destroy();
__f.__f_ = __as_base(&__f.__buf_);
}
else if (__f_)
{
__f_->__move(__as_base(&__f.__buf_));
__f_->destroy();
__f_ = 0;
__f.__f_ = __as_base(&__f.__buf_);
}
else if (__f.__f_)
{
__f.__f_->__move(__as_base(&__buf_));
__f.__f_->destroy();
__f.__f_ = 0;
__f_ = __as_base(&__buf_);
}
}
template<class _Rp, class ..._ArgTypes>
_Rp
function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const
{
if (__f_ == 0)
__throw_bad_function_call();
return (*__f_)(wistd::forward<_ArgTypes>(__arg)...);
}
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
bool
operator==(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT {return !__f;}
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
bool
operator==(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT {return !__f;}
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
bool
operator!=(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT {return (bool)__f;}
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
bool
operator!=(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT {return (bool)__f;}
// Provide both 'swap_wil' and 'swap' since we now have two ADL scenarios that we need to work
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
void
swap(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y)
{return __x.swap(__y);}
template <class _Rp, class... _ArgTypes>
inline __WI_LIBCPP_INLINE_VISIBILITY
void
swap_wil(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y)
{return __x.swap(__y);}
// std::invoke
template <class _Fn, class ..._Args>
typename __invoke_of<_Fn, _Args...>::type
invoke(_Fn&& __f, _Args&&... __args)
__WI_NOEXCEPT_((__nothrow_invokable<_Fn, _Args...>::value))
{
return wistd::__invoke(wistd::forward<_Fn>(__f), wistd::forward<_Args>(__args)...);
}
#else // __WI_LIBCPP_CXX03_LANG
#error wistd::function and wistd::invoke not implemented for pre-C++11
#endif
}
/// @endcond
#pragma warning(pop)
#endif // _WISTD_FUNCTIONAL_H_

1038
Externals/WIL/include/wil/wistd_memory.h vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

84
Externals/WIL/include/wil/wrl.h vendored Normal file
View file

@ -0,0 +1,84 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_WRL_INCLUDED
#define __WIL_WRL_INCLUDED
#include <wrl.h>
#include "result.h"
#include "common.h" // wistd type_traits helpers
namespace wil
{
#ifdef WIL_ENABLE_EXCEPTIONS
#pragma region Object construction helpers that throw exceptions
/** Used to construct a RuntimeClass based object that uses 2 phase construction.
Construct a RuntimeClass based object that uses 2 phase construction (by implementing
RuntimeClassInitialize() and returning error codes for failures.
~~~~
// SomeClass uses 2 phase initialization by implementing RuntimeClassInitialize()
auto someClass = MakeAndInitializeOrThrow<SomeClass>(L"input", true);
~~~~ */
template <typename T, typename... TArgs>
Microsoft::WRL::ComPtr<T> MakeAndInitializeOrThrow(TArgs&&... args)
{
Microsoft::WRL::ComPtr<T> obj;
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<T>(&obj, Microsoft::WRL::Details::Forward<TArgs>(args)...));
return obj;
}
/** Used to construct an RuntimeClass based object that uses exceptions in its constructor (and does
not require 2 phase construction).
~~~~
// SomeClass uses exceptions for error handling in its constructor.
auto someClass = MakeOrThrow<SomeClass>(L"input", true);
~~~~ */
template <typename T, typename... TArgs>
Microsoft::WRL::ComPtr<T> MakeOrThrow(TArgs&&... args)
{
// This is how you can detect the presence of RuntimeClassInitialize() and find dangerous use.
// Unfortunately this produces false positives as all RuntimeClass derived classes have
// a RuntimeClassInitialize() method from their base class.
// static_assert(!std::is_member_function_pointer<decltype(&T::RuntimeClassInitialize)>::value,
// "class has a RuntimeClassInitialize member, use MakeAndInitializeOrThrow instead");
auto obj = Microsoft::WRL::Make<T>(Microsoft::WRL::Details::Forward<TArgs>(args)...);
THROW_IF_NULL_ALLOC(obj.Get());
return obj;
}
#pragma endregion
#endif // WIL_ENABLE_EXCEPTIONS
/** By default WRL Callback objects are not agile, use this to make an agile one. Replace use of Callback<> with MakeAgileCallback<>.
Will return null on failure, translate that into E_OUTOFMEMORY using XXX_IF_NULL_ALLOC()
from wil\result.h to test the result. */
template<typename TDelegateInterface, typename ...Args>
::Microsoft::WRL::ComPtr<TDelegateInterface> MakeAgileCallbackNoThrow(Args&&... args) WI_NOEXCEPT
{
using namespace Microsoft::WRL;
return Callback<Implements<RuntimeClassFlags<ClassicCom>, TDelegateInterface, FtmBase>>(wistd::forward<Args>(args)...);
}
#ifdef WIL_ENABLE_EXCEPTIONS
template<typename TDelegateInterface, typename ...Args>
::Microsoft::WRL::ComPtr<TDelegateInterface> MakeAgileCallback(Args&&... args)
{
auto result = MakeAgileCallbackNoThrow<TDelegateInterface, Args...>(wistd::forward<Args>(args)...);
THROW_IF_NULL_ALLOC(result);
return result;
}
#endif // WIL_ENABLE_EXCEPTIONS
} // namespace wil
#endif // __WIL_WRL_INCLUDED

View file

@ -0,0 +1,2 @@
add_subdirectory(nuget)

View file

@ -0,0 +1,20 @@
file(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}/build_tools/nuget.exe" nuget_exe)
file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}" nupkg_dir)
file(TO_NATIVE_PATH "${nupkg_dir}/Microsoft.Windows.ImplementationLibrary.${WIL_BUILD_VERSION}.nupkg" wil_nupkg)
# The build servers don't have an up-to-date version of nuget, so pull it down ourselves...
file(DOWNLOAD https://dist.nuget.org/win-x86-commandline/latest/nuget.exe ${nuget_exe})
file(GLOB_RECURSE wil_headers ${CMAKE_SOURCE_DIR}/include/*.h)
add_custom_command(OUTPUT ${wil_nupkg}
COMMAND ${nuget_exe} pack ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec -OutputDirectory ${nupkg_dir} -Version ${WIL_BUILD_VERSION} -NonInteractive
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec
${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.targets
${wil_headers}
${CMAKE_SOURCE_DIR}/LICENSE
${CMAKE_SOURCE_DIR}/ThirdPartyNotices.txt)
add_custom_target(make_wil_nupkg DEPENDS ${wil_nupkg})

View file

@ -0,0 +1,21 @@
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>Microsoft.Windows.ImplementationLibrary</id>
<version>0.0.0</version>
<title>Windows Implementation Library</title>
<authors>Microsoft</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>The Windows Implementation Libraries (wil) were created to improve productivity and solve problems commonly seen by Windows developers.</description>
<tags>windows utility wil native</tags>
<copyright>© Microsoft Corporation. All rights reserved.</copyright>
<license type="file">LICENSE</license>
<projectUrl>https://github.com/Microsoft/wil</projectUrl>
</metadata>
<files>
<file src="..\..\LICENSE"/>
<file src="..\..\ThirdPartyNotices.txt"/>
<file src="..\..\include\**" target="include\" />
<file src="Microsoft.Windows.ImplementationLibrary.targets" target="build\native\" />
</files>
</package>

View file

@ -0,0 +1,8 @@
<?xml version="1.0"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
</Project>

View file

@ -0,0 +1,41 @@
# Windows Implementation Library Pipeline
trigger:
- master
jobs:
- job: BuildAndTest
timeoutInMinutes: 360
pool:
vmImage: 'windows-2019'
steps:
- script: |
choco install llvm
if %ERRORLEVEL% NEQ 0 goto :eof
echo ##vso[task.setvariable variable=PATH]%PATH%;C:\Program Files\LLVM\bin
displayName: 'Install Clang'
- script: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars32.bat"
if %ERRORLEVEL% NEQ 0 goto :eof
call scripts\init_all.cmd --fast
if %ERRORLEVEL% NEQ 0 goto :eof
call scripts\build_all.cmd
displayName: 'Build x86'
- script: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
if %ERRORLEVEL% NEQ 0 goto :eof
call scripts\init_all.cmd --fast
if %ERRORLEVEL% NEQ 0 goto :eof
call scripts\build_all.cmd
displayName: 'Build x64'
- script: call scripts\runtests.cmd
displayName: 'Run Tests'

51
Externals/WIL/scripts/build_all.cmd vendored Normal file
View file

@ -0,0 +1,51 @@
@echo off
setlocal EnableDelayedExpansion
set BUILD_ROOT=%~dp0\..\build
if "%Platform%"=="x64" (
set BUILD_ARCH=64
) else if "%Platform%"=="x86" (
set BUILD_ARCH=32
) else if [%Platform%]==[] (
echo ERROR: The build_all.cmd script must be run from a Visual Studio command window
exit /B 1
) else (
echo ERROR: Unrecognized/unsupported platform %Platform%
exit /B 1
)
call :build clang debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build clang release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build clang relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build clang minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build msvc debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build msvc release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build msvc relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build msvc minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
echo All build completed successfully!
goto :eof
:: build [compiler] [type]
:build
set BUILD_DIR=%BUILD_ROOT%\%1%BUILD_ARCH%%2
if not exist %BUILD_DIR% (
goto :eof
)
pushd %BUILD_DIR%
echo Building from %CD%
ninja
popd
goto :eof

202
Externals/WIL/scripts/init.cmd vendored Normal file
View file

@ -0,0 +1,202 @@
@echo off
setlocal
setlocal EnableDelayedExpansion
:: Globals
set BUILD_ROOT=%~dp0\..\build
goto :init
:usage
echo USAGE:
echo init.cmd [--help] [-c^|--compiler ^<clang^|msvc^>] [-g^|--generator ^<ninja^|msbuild^>]
echo [-b^|--build-type ^<debug^|release^|relwithdebinfo^|minsizerel^>] [-l^|--linker link^|lld-link]
echo [--fast] [-v^|--version X.Y.Z]
echo.
echo ARGUMENTS
echo -c^|--compiler Controls the compiler used, either 'clang' (the default) or 'msvc'
echo -g^|--generator Controls the CMake generator used, either 'ninja' (the default) or 'msbuild'
echo -b^|--build-type Controls the value of 'CMAKE_BUILD_TYPE', either 'debug' (the default), 'release',
echo 'relwithdebinfo', or 'minsizerel'
echo -v^|--version Specifies the version of the NuGet package produced. Primarily only used by the CI
echo build and is typically not necessary when building locally
echo --fast Used to (slightly) reduce compile times and build output size. This is primarily
echo used by the CI build machines where resources are more constrained. This switch is
echo temporary and will be removed once https://github.com/microsoft/wil/issues/9 is fixed
goto :eof
:init
:: Initialize values as empty so that we can identify if we are using defaults later for error purposes
set COMPILER=
set GENERATOR=
set BUILD_TYPE=
set LINKER=
set CMAKE_ARGS=
set BITNESS=
set VERSION=
set FAST_BUILD=0
:parse
if /I "%~1"=="" goto :execute
if /I "%~1"=="--help" call :usage & goto :eof
set COMPILER_SET=0
if /I "%~1"=="-c" set COMPILER_SET=1
if /I "%~1"=="--compiler" set COMPILER_SET=1
if %COMPILER_SET%==1 (
if "%COMPILER%" NEQ "" echo ERROR: Compiler already specified & call :usage & exit /B 1
if /I "%~2"=="clang" set COMPILER=clang
if /I "%~2"=="msvc" set COMPILER=msvc
if "!COMPILER!"=="" echo ERROR: Unrecognized/missing compiler %~2 & call :usage & exit /B 1
shift
shift
goto :parse
)
set GENERATOR_SET=0
if /I "%~1"=="-g" set GENERATOR_SET=1
if /I "%~1"=="--generator" set GENERATOR_SET=1
if %GENERATOR_SET%==1 (
if "%GENERATOR%" NEQ "" echo ERROR: Generator already specified & call :usage & exit /B 1
if /I "%~2"=="ninja" set GENERATOR=ninja
if /I "%~2"=="msbuild" set GENERATOR=msbuild
if "!GENERATOR!"=="" echo ERROR: Unrecognized/missing generator %~2 & call :usage & exit /B 1
shift
shift
goto :parse
)
set BUILD_TYPE_SET=0
if /I "%~1"=="-b" set BUILD_TYPE_SET=1
if /I "%~1"=="--build-type" set BUILD_TYPE_SET=1
if %BUILD_TYPE_SET%==1 (
if "%BUILD_TYPE%" NEQ "" echo ERROR: Build type already specified & call :usage & exit /B 1
if /I "%~2"=="debug" set BUILD_TYPE=debug
if /I "%~2"=="release" set BUILD_TYPE=release
if /I "%~2"=="relwithdebinfo" set BUILD_TYPE=relwithdebinfo
if /I "%~2"=="minsizerel" set BUILD_TYPE=minsizerel
if "!BUILD_TYPE!"=="" echo ERROR: Unrecognized/missing build type %~2 & call :usage & exit /B 1
shift
shift
goto :parse
)
set LINKER_SET=0
if /I "%~1"=="-l" set LINKER_SET=1
if /I "%~1"=="--linker" set LINKER_SET=1
if %LINKER_SET%==1 (
if "%LINKER%" NEQ "" echo ERROR: Linker already specified & call :usage & exit /B 1
if /I "%~2"=="link" set LINKER=link
if /I "%~2"=="lld-link" set LINKER=lld-link
if "!LINKER!"=="" echo ERROR: Unrecognized/missing linker %~2 & call :usage & exit /B 1
shift
shift
goto :parse
)
set VERSION_SET=0
if /I "%~1"=="-v" set VERSION_SET=1
if /I "%~1"=="--version" set VERSION_SET=1
if %VERSION_SET%==1 (
if "%VERSION%" NEQ "" echo ERROR: Version alread specified & call :usage & exit /B 1
if /I "%~2"=="" echo ERROR: Version string missing & call :usage & exit /B 1
set VERSION=%~2
shift
shift
goto :parse
)
if /I "%~1"=="--fast" (
if %FAST_BUILD% NEQ 0 echo ERROR: Fast build already specified
set FAST_BUILD=1
shift
goto :parse
)
echo ERROR: Unrecognized argument %~1
call :usage
exit /B 1
:execute
:: Check for conflicting arguments
if "%GENERATOR%"=="msbuild" (
if "%COMPILER%"=="clang" echo ERROR: Cannot use Clang with MSBuild & exit /B 1
:: While CMake won't give an error, specifying the linker won't actually have any effect with the VS generator
if "%LINKER%"=="lld-link" echo ERROR: Cannot use lld-link with MSBuild & exit /B 1
)
:: Select defaults
if "%GENERATOR%"=="" set GENERATOR=ninja
if %GENERATOR%==msbuild set COMPILER=msvc
if "%COMPILER%"=="" set COMPILER=clang
if "%BUILD_TYPE%"=="" set BUILD_TYPE=debug
if "%LINKER%"=="" set LINKER=link
:: Formulate CMake arguments
if %GENERATOR%==ninja set CMAKE_ARGS=%CMAKE_ARGS% -G Ninja
if %COMPILER%==clang set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
if %COMPILER%==msvc set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl
if %GENERATOR% NEQ msbuild (
if %BUILD_TYPE%==debug set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=Debug
if %BUILD_TYPE%==release set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=Release
if %BUILD_TYPE%==relwithdebinfo set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=RelWithDebInfo
if %BUILD_TYPE%==minsizerel set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=MinSizeRel
) else (
:: The Visual Studio generator, by default, will use the most recent Windows SDK version installed that is not
:: greater than the host OS version. This decision is to ensure that anything built will be able to run on the
:: machine that built it. This experience is generally okay, if not desired, but affects us since we build with
:: '/permissive-' etc. and older versions of the SDK are typically not as clean as the most recent versions.
:: This flag will force the generator to select the most recent SDK version independent of host OS version.
set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_SYSTEM_VERSION=10.0
)
if %LINKER%==lld-link (
set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_LINKER=lld-link
)
if "%VERSION%" NEQ "" set CMAKE_ARGS=%CMAKE_ARGS% -DWIL_BUILD_VERSION=%VERSION%
if %FAST_BUILD%==1 set CMAKE_ARGS=%CMAKE_ARGS% -DFAST_BUILD=ON
:: Figure out the platform
if "%Platform%"=="" echo ERROR: The init.cmd script must be run from a Visual Studio command window & exit /B 1
if "%Platform%"=="x86" (
set BITNESS=32
if %COMPILER%==clang set CFLAGS=-m32 & set CXXFLAGS=-m32
)
if "%Platform%"=="x64" set BITNESS=64
if "%BITNESS%"=="" echo ERROR: Unrecognized/unsupported platform %Platform% & exit /B 1
:: Set up the build directory
set BUILD_DIR=%BUILD_ROOT%\%COMPILER%%BITNESS%%BUILD_TYPE%
mkdir %BUILD_DIR% > NUL 2>&1
:: Run CMake
pushd %BUILD_DIR%
echo Using compiler....... %COMPILER%
echo Using linker......... %LINKER%
echo Using architecture... %Platform%
echo Using build type..... %BUILD_TYPE%
echo Using build root..... %CD%
echo.
cmake %CMAKE_ARGS% ..\..
popd
goto :eof

13
Externals/WIL/scripts/init_all.cmd vendored Normal file
View file

@ -0,0 +1,13 @@
@echo off
:: NOTE: Architecture is picked up from the command window, so we can't control that here :(
call %~dp0\init.cmd -c clang -g ninja -b debug %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call %~dp0\init.cmd -c clang -g ninja -b relwithdebinfo %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call %~dp0\init.cmd -c msvc -g ninja -b debug %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call %~dp0\init.cmd -c msvc -g ninja -b relwithdebinfo %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )

67
Externals/WIL/scripts/runtests.cmd vendored Normal file
View file

@ -0,0 +1,67 @@
@echo off
setlocal EnableDelayedExpansion
set BUILD_ROOT=%~dp0\..\build
:: Unlike building, we don't need to limit ourselves to the Platform of the command window
call :execute_tests clang64debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang64release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang64relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang64minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang32debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang32release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang32relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang32minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc64debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc64release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc64relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc64minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc32debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc32release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc32relwithdebinfo
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests msvc32minsizerel
if %ERRORLEVEL% NEQ 0 ( goto :eof )
goto :eof
:execute_tests
set BUILD_DIR=%BUILD_ROOT%\%1
if not exist %BUILD_DIR% ( goto :eof )
pushd %BUILD_DIR%
echo Running tests from %CD%
call :execute_test app witest.app.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
call :execute_test cpplatest witest.cpplatest.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
call :execute_test noexcept witest.noexcept.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
call :execute_test normal witest.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
popd
goto :eof
:execute_test
if not exist tests\%1\%2 ( goto :eof )
echo Running %1 tests...
tests\%1\%2
goto :eof

19
Externals/WIL/tests/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,19 @@
include(${CMAKE_SOURCE_DIR}/cmake/common_build_flags.cmake)
# All projects need to reference the WIL headers
include_directories(${CMAKE_SOURCE_DIR}/include)
# TODO: Might be worth trying to conditionally do this on SDK version, assuming there's a semi-easy way to detect that
include_directories(BEFORE SYSTEM ./workarounds/wrl)
# The build pipelines have limitations that local development environments do not, so turn a few knobs
if (${FAST_BUILD})
replace_cxx_flag("/GR" "/GR-") # Disables RTTI
add_definitions(-DCATCH_CONFIG_FAST_COMPILE -DWIL_FAST_BUILD)
endif()
add_subdirectory(app)
add_subdirectory(cpplatest)
add_subdirectory(noexcept)
add_subdirectory(normal)

2717
Externals/WIL/tests/ComTests.cpp vendored Normal file

File diff suppressed because it is too large Load diff

243
Externals/WIL/tests/CommonTests.cpp vendored Normal file
View file

@ -0,0 +1,243 @@
#include <wil/common.h>
#include <wil/resource.h>
#include <wrl/client.h>
#include "common.h"
TEST_CASE("CommonTests::OutParamHelpers", "[common]")
{
int i = 2;
int *pOutTest = &i;
int *pNullTest = nullptr;
SECTION("Value type")
{
wil::assign_to_opt_param(pNullTest, 3);
wil::assign_to_opt_param(pOutTest, 3);
REQUIRE(*pOutTest == 3);
}
SECTION("Pointer to value type")
{
int **ppOutTest = &pOutTest;
int **ppNullTest = nullptr;
wil::assign_null_to_opt_param(ppNullTest);
wil::assign_null_to_opt_param(ppOutTest);
REQUIRE(*ppOutTest == nullptr);
}
SECTION("COM out pointer")
{
Microsoft::WRL::ComPtr<IUnknown> spUnk;
IUnknown **ppunkNull = nullptr;
IUnknown *pUnk = reinterpret_cast<IUnknown *>(1);
IUnknown **ppUnkValid = &pUnk;
wil::detach_to_opt_param(ppunkNull, spUnk);
wil::detach_to_opt_param(ppUnkValid, spUnk);
REQUIRE(*ppUnkValid == nullptr);
}
}
TEST_CASE("CommonTests::TypeValidation", "[common]")
{
std::unique_ptr<BYTE> boolCastClass;
std::vector<int> noBoolCastClass;
HRESULT hr = S_OK;
BOOL bigBool = true;
bool smallBool = true;
DWORD dword = 1;
Microsoft::WRL::ComPtr<IUnknown> comPtr;
(void)dword;
// NOTE: The commented out verify* calls should give compilation errors
SECTION("verify_bool")
{
REQUIRE(wil::verify_bool(smallBool));
REQUIRE(wil::verify_bool(bigBool));
REQUIRE_FALSE(wil::verify_bool(boolCastClass));
REQUIRE_FALSE(wil::verify_bool(comPtr));
//wil::verify_bool(noBoolCastClass);
//wil::verify_bool(dword);
//wil::verify_bool(hr);
}
SECTION("verify_hresult")
{
//wil::verify_hresult(smallBool);
//wil::verify_hresult(bigBool);
//wil::verify_hresult(boolCastClass);
//wil::verify_hresult(noBoolCastClass);
//wil::verify_hresult(dword);
//wil::verify_hresult(comPtr);
REQUIRE(wil::verify_hresult(hr) == S_OK);
}
SECTION("verify_BOOL")
{
//wil::verify_BOOL(smallBool);
REQUIRE(wil::verify_BOOL(bigBool));
//wil::verify_BOOL(boolCastClass);
//wil::verify_BOOL(noBoolCastClass);
//wil::verify_BOOL(dword);
//wil::verify_BOOL(comPtr);
//wil::verify_BOOL(hr);
}
}
template <typename T>
static void FlagsMacrosNonStatic(T none, T one, T two, T three, T four)
{
T eval = one | four;
REQUIRE(WI_AreAllFlagsSet(MDEC(eval), MDEC(one | four)));
REQUIRE_FALSE(WI_AreAllFlagsSet(eval, one | three));
REQUIRE_FALSE(WI_AreAllFlagsSet(eval, three | two));
REQUIRE(WI_AreAllFlagsSet(eval, none));
REQUIRE(WI_IsAnyFlagSet(MDEC(eval), MDEC(one)));
REQUIRE(WI_IsAnyFlagSet(eval, one | four | three));
REQUIRE_FALSE(WI_IsAnyFlagSet(eval, two));
REQUIRE(WI_AreAllFlagsClear(MDEC(eval), MDEC(three)));
REQUIRE(WI_AreAllFlagsClear(eval, three | two));
REQUIRE_FALSE(WI_AreAllFlagsClear(eval, one | four));
REQUIRE_FALSE(WI_AreAllFlagsClear(eval, one | three));
REQUIRE(WI_IsAnyFlagClear(MDEC(eval), MDEC(three)));
REQUIRE(WI_IsAnyFlagClear(eval, three | two));
REQUIRE(WI_IsAnyFlagClear(eval, four | three));
REQUIRE_FALSE(WI_IsAnyFlagClear(eval, one));
REQUIRE_FALSE(WI_IsAnyFlagClear(eval, one | four));
REQUIRE_FALSE(WI_IsSingleFlagSet(MDEC(eval)));
REQUIRE(WI_IsSingleFlagSet(eval & one));
REQUIRE(WI_IsSingleFlagSetInMask(MDEC(eval), MDEC(one)));
REQUIRE(WI_IsSingleFlagSetInMask(eval, one | three));
REQUIRE_FALSE(WI_IsSingleFlagSetInMask(eval, three));
REQUIRE_FALSE(WI_IsSingleFlagSetInMask(eval, one | four));
REQUIRE_FALSE(WI_IsClearOrSingleFlagSet(MDEC(eval)));
REQUIRE(WI_IsClearOrSingleFlagSet(eval & one));
REQUIRE(WI_IsClearOrSingleFlagSet(none));
REQUIRE(WI_IsClearOrSingleFlagSetInMask(MDEC(eval), MDEC(one)));
REQUIRE(WI_IsClearOrSingleFlagSetInMask(eval, one | three));
REQUIRE(WI_IsClearOrSingleFlagSetInMask(eval, three));
REQUIRE_FALSE(WI_IsClearOrSingleFlagSetInMask(eval, one | four));
eval = none;
WI_SetAllFlags(MDEC(eval), MDEC(one));
REQUIRE(eval == one);
WI_SetAllFlags(eval, one | two);
REQUIRE(eval == (one | two));
eval = one | two;
WI_ClearAllFlags(MDEC(eval), one);
REQUIRE(eval == two);
WI_ClearAllFlags(eval, two);
REQUIRE(eval == none);
eval = one | two;
WI_UpdateFlagsInMask(MDEC(eval), MDEC(two | three), MDEC(three | four));
REQUIRE(eval == (one | three));
eval = one;
WI_ToggleAllFlags(MDEC(eval), MDEC(one | two));
REQUIRE(eval == two);
}
enum class EClassTest
{
None = 0x0,
One = 0x1,
Two = 0x2,
Three = 0x4,
Four = 0x8,
};
DEFINE_ENUM_FLAG_OPERATORS(EClassTest);
enum ERawTest
{
ER_None = 0x0,
ER_One = 0x1,
ER_Two = 0x2,
ER_Three = 0x4,
ER_Four = 0x8,
};
DEFINE_ENUM_FLAG_OPERATORS(ERawTest);
TEST_CASE("CommonTests::FlagsMacros", "[common]")
{
SECTION("Integral types")
{
FlagsMacrosNonStatic<char>(static_cast<char>(0), static_cast<char>(0x1), static_cast<char>(0x2), static_cast<char>(0x4), static_cast<char>(0x40));
FlagsMacrosNonStatic<unsigned char>(0, 0x1, 0x2, 0x4, 0x80u);
FlagsMacrosNonStatic<short>(0, 0x1, 0x2, 0x4, 0x4000);
FlagsMacrosNonStatic<unsigned short>(0, 0x1, 0x2, 0x4, 0x8000u);
FlagsMacrosNonStatic<long>(0, 0x1, 0x2, 0x4, 0x80000000ul);
FlagsMacrosNonStatic<unsigned long>(0, 0x1, 0x2, 0x4, 0x80000000ul);
FlagsMacrosNonStatic<long long>(0, 0x1, 0x2, 0x4, 0x8000000000000000ull);
FlagsMacrosNonStatic<unsigned long long>(0, 0x1, 0x2, 0x4, 0x8000000000000000ull);
}
SECTION("Raw enum")
{
FlagsMacrosNonStatic<ERawTest>(ER_None, ER_One, ER_Two, ER_Three, ER_Four);
}
SECTION("Enum class")
{
FlagsMacrosNonStatic<EClassTest>(EClassTest::None, EClassTest::One, EClassTest::Two, EClassTest::Three, EClassTest::Four);
EClassTest eclass = EClassTest::One | EClassTest::Two;
REQUIRE(WI_IsFlagSet(MDEC(eclass), EClassTest::One));
REQUIRE(WI_IsFlagSet(eclass, EClassTest::Two));
REQUIRE_FALSE(WI_IsFlagSet(eclass, EClassTest::Three));
REQUIRE(WI_IsFlagClear(MDEC(eclass), EClassTest::Three));
REQUIRE_FALSE(WI_IsFlagClear(eclass, EClassTest::One));
REQUIRE_FALSE(WI_IsSingleFlagSet(MDEC(eclass)));
REQUIRE(WI_IsSingleFlagSet(eclass & EClassTest::One));
eclass = EClassTest::None;
WI_SetFlag(MDEC(eclass), EClassTest::One);
REQUIRE(eclass == EClassTest::One);
eclass = EClassTest::None;
WI_SetFlagIf(eclass, EClassTest::One, false);
REQUIRE(eclass == EClassTest::None);
WI_SetFlagIf(eclass, EClassTest::One, true);
REQUIRE(eclass == EClassTest::One);
eclass = EClassTest::None;
WI_SetFlagIf(eclass, EClassTest::One, false);
REQUIRE(eclass == EClassTest::None);
WI_SetFlagIf(eclass, EClassTest::One, true);
REQUIRE(eclass == EClassTest::One);
eclass = EClassTest::One | EClassTest::Two;
WI_ClearFlag(eclass, EClassTest::Two);
REQUIRE(eclass == EClassTest::One);
eclass = EClassTest::One | EClassTest::Two;
WI_ClearFlagIf(eclass, EClassTest::One, false);
REQUIRE(eclass == (EClassTest::One | EClassTest::Two));
WI_ClearFlagIf(eclass, EClassTest::One, true);
REQUIRE(eclass == EClassTest::Two);
eclass = EClassTest::None;
WI_UpdateFlag(eclass, EClassTest::One, true);
REQUIRE(eclass == EClassTest::One);
WI_UpdateFlag(eclass, EClassTest::One, false);
REQUIRE(eclass == EClassTest::None);
eclass = EClassTest::One;
WI_ToggleFlag(eclass, EClassTest::One);
WI_ToggleFlag(eclass, EClassTest::Two);
REQUIRE(eclass == EClassTest::Two);
}
}

28
Externals/WIL/tests/CppWinRT20Tests.cpp vendored Normal file
View file

@ -0,0 +1,28 @@
// Prior to C++/WinRT 2.0 this would cause issues since we're not including wil/cppwinrt.h in this translation unit.
// However, since we're going to link into the same executable as 'CppWinRTTests.cpp', the 'winrt_to_hresult_handler'
// global function pointer should be set, so these should all run successfully
#include <winrt/base.h>
#include <wil/result.h>
#include "common.h"
TEST_CASE("CppWinRTTests::CppWinRT20Test", "[cppwinrt]")
{
auto test = [](HRESULT hr)
{
try
{
THROW_HR(hr);
}
catch (...)
{
REQUIRE(hr == winrt::to_hresult());
}
};
test(E_OUTOFMEMORY);
test(E_INVALIDARG);
test(E_UNEXPECTED);
}

144
Externals/WIL/tests/CppWinRTTests.cpp vendored Normal file
View file

@ -0,0 +1,144 @@
#include <wil/cppwinrt.h>
#include "catch.hpp"
// HRESULT values that C++/WinRT throws as something other than winrt::hresult_error - e.g. a type derived from
// winrt::hresult_error, std::*, etc.
static const HRESULT cppwinrt_mapped_hresults[] =
{
E_ACCESSDENIED,
RPC_E_WRONG_THREAD,
E_NOTIMPL,
E_INVALIDARG,
E_BOUNDS,
E_NOINTERFACE,
CLASS_E_CLASSNOTAVAILABLE,
E_CHANGED_STATE,
E_ILLEGAL_METHOD_CALL,
E_ILLEGAL_STATE_CHANGE,
E_ILLEGAL_DELEGATE_ASSIGNMENT,
HRESULT_FROM_WIN32(ERROR_CANCELLED),
E_OUTOFMEMORY,
};
TEST_CASE("CppWinRTTests::WilToCppWinRTExceptionTranslationTest", "[cppwinrt]")
{
auto test = [](HRESULT hr)
{
try
{
THROW_HR(hr);
}
catch (...)
{
REQUIRE(hr == winrt::to_hresult());
}
};
for (auto hr : cppwinrt_mapped_hresults)
{
test(hr);
}
// A non-mapped HRESULT
test(E_UNEXPECTED);
}
TEST_CASE("CppWinRTTests::CppWinRTToWilExceptionTranslationTest", "[cppwinrt]")
{
auto test = [](HRESULT hr)
{
try
{
winrt::check_hresult(hr);
}
catch (...)
{
REQUIRE(hr == wil::ResultFromCaughtException());
}
};
for (auto hr : cppwinrt_mapped_hresults)
{
test(hr);
}
// A non-mapped HRESULT
test(E_UNEXPECTED);
}
TEST_CASE("CppWinRTTests::ResultFromExceptionDebugTest", "[cppwinrt]")
{
auto test = [](HRESULT hr, wil::SupportedExceptions supportedExceptions)
{
auto result = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, supportedExceptions, [&]()
{
winrt::check_hresult(hr);
});
REQUIRE(hr == result);
};
for (auto hr : cppwinrt_mapped_hresults)
{
test(hr, wil::SupportedExceptions::Known);
test(hr, wil::SupportedExceptions::All);
}
// A non-mapped HRESULT
test(E_UNEXPECTED, wil::SupportedExceptions::Known);
test(E_UNEXPECTED, wil::SupportedExceptions::All);
// Uncomment any of the following to validate SEH failfast
//test(E_UNEXPECTED, wil::SupportedExceptions::None);
//test(E_ACCESSDENIED, wil::SupportedExceptions::Thrown);
//test(E_INVALIDARG, wil::SupportedExceptions::ThrownOrAlloc);
}
TEST_CASE("CppWinRTTests::CppWinRTConsistencyTest", "[cppwinrt]")
{
// Since setting 'winrt_to_hresult_handler' opts us into _all_ C++/WinRT exception translation handling, we need to
// make sure that we preserve behavior, at least with 'check_hresult', especially when C++/WinRT maps a particular
// HRESULT value to a different exception type
auto test = [](HRESULT hr)
{
try
{
winrt::check_hresult(hr);
}
catch (...)
{
REQUIRE(hr == winrt::to_hresult());
}
};
for (auto hr : cppwinrt_mapped_hresults)
{
test(hr);
}
// A non-mapped HRESULT
test(E_UNEXPECTED);
// C++/WinRT also maps a few std::* exceptions to various HRESULTs. We should preserve this behavior
try
{
throw std::out_of_range("oopsie");
}
catch (...)
{
REQUIRE(winrt::to_hresult() == E_BOUNDS);
}
try
{
throw std::invalid_argument("daisy");
}
catch (...)
{
REQUIRE(winrt::to_hresult() == E_INVALIDARG);
}
// NOTE: C++/WinRT maps other 'std::exception' derived exceptions to E_FAIL, however we preserve the WIL behavior
// that such exceptions become HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)
}

400
Externals/WIL/tests/FakeWinRTTypes.h vendored Normal file
View file

@ -0,0 +1,400 @@
#pragma once
#include <wil/resource.h>
#include <windows.foundation.h>
#include <wrl/implements.h>
#include <wrl/wrappers/corewrappers.h>
template <typename T>
struct WinRTStorage
{
T value = {};
HRESULT CopyTo(T* result)
{
*result = value;
return S_OK;
}
HRESULT Set(T val)
{
value = val;
return S_OK;
}
static void Destroy(T&)
{
}
void Reset()
{
}
bool Equals(T val)
{
// NOTE: Padding can through this off, but this isn't intended to be a robust solution...
return memcmp(&value, &val, sizeof(T)) == 0;
}
};
template <>
struct WinRTStorage<HSTRING>
{
Microsoft::WRL::Wrappers::HString value;
HRESULT CopyTo(HSTRING* result)
{
return value.CopyTo(result);
}
HRESULT Set(HSTRING val)
{
return value.Set(val);
}
static void Destroy(HSTRING& val)
{
::WindowsDeleteString(val);
val = nullptr;
}
void Reset()
{
value = {};
}
bool Equals(HSTRING val)
{
return value == val;
}
};
template <typename T>
struct WinRTStorage<T*>
{
Microsoft::WRL::ComPtr<T> value;
HRESULT CopyTo(T** result)
{
*result = Microsoft::WRL::ComPtr<T>(value).Detach();
return S_OK;
}
HRESULT Set(T* val)
{
value = val;
return S_OK;
}
static void Destroy(T*& val)
{
val->Release();
val = nullptr;
}
void Reset()
{
value.Reset();
}
bool Equals(T* val)
{
return value.Get() == val;
}
};
// Very minimal IAsyncOperation implementation that gives calling tests control over when it completes
template <typename Logical, typename Abi = Logical>
struct FakeAsyncOperation : Microsoft::WRL::RuntimeClass<
ABI::Windows::Foundation::IAsyncInfo,
ABI::Windows::Foundation::IAsyncOperation<Logical>>
{
using Handler = ABI::Windows::Foundation::IAsyncOperationCompletedHandler<Logical>;
// IAsyncInfo
IFACEMETHODIMP get_Id(unsigned int*) override
{
return E_NOTIMPL;
}
IFACEMETHODIMP get_Status(AsyncStatus* status) override
{
auto lock = m_lock.lock_shared();
*status = m_status;
return S_OK;
}
IFACEMETHODIMP get_ErrorCode(HRESULT* errorCode) override
{
auto lock = m_lock.lock_shared();
*errorCode = m_result;
return S_OK;
}
IFACEMETHODIMP Cancel() override
{
return E_NOTIMPL;
}
IFACEMETHODIMP Close() override
{
return E_NOTIMPL;
}
// IAsyncOperation
IFACEMETHODIMP put_Completed(Handler* handler) override
{
bool invoke = false;
{
auto lock = m_lock.lock_exclusive();
if (m_handler)
{
return E_FAIL;
}
m_handler = handler;
invoke = m_status != ABI::Windows::Foundation::AsyncStatus::Started;
}
if (invoke)
{
handler->Invoke(this, m_status);
}
return S_OK;
}
IFACEMETHODIMP get_Completed(Handler** handler) override
{
auto lock = m_lock.lock_shared();
*handler = Microsoft::WRL::ComPtr<Handler>(m_handler).Detach();
return S_OK;
}
IFACEMETHODIMP GetResults(Abi* results) override
{
return m_storage.CopyTo(results);
}
// Test functions
void Complete(HRESULT hr, Abi result)
{
using namespace ABI::Windows::Foundation;
Handler* handler = nullptr;
{
auto lock = m_lock.lock_exclusive();
if (m_status == AsyncStatus::Started)
{
m_result = hr;
m_storage.Set(result);
m_status = SUCCEEDED(hr) ? AsyncStatus::Completed : AsyncStatus::Error;
handler = m_handler.Get();
}
}
if (handler)
{
handler->Invoke(this, m_status);
}
}
private:
wil::srwlock m_lock;
Microsoft::WRL::ComPtr<Handler> m_handler;
ABI::Windows::Foundation::AsyncStatus m_status = ABI::Windows::Foundation::AsyncStatus::Started;
HRESULT m_result = S_OK;
WinRTStorage<Abi> m_storage;
};
template <typename Logical, typename Abi = Logical, size_t MaxSize = 42>
struct FakeVector : Microsoft::WRL::RuntimeClass<
ABI::Windows::Foundation::Collections::IVector<Logical>,
ABI::Windows::Foundation::Collections::IVectorView<Logical>>
{
// IVector
IFACEMETHODIMP GetAt(unsigned index, Abi* item) override
{
if (index >= m_size)
{
return E_BOUNDS;
}
return m_data[index].CopyTo(item);
}
IFACEMETHODIMP get_Size(unsigned* size) override
{
*size = static_cast<unsigned>(m_size);
return S_OK;
}
IFACEMETHODIMP GetView(ABI::Windows::Foundation::Collections::IVectorView<Logical>** view) override
{
this->AddRef();
*view = this;
return S_OK;
}
IFACEMETHODIMP IndexOf(Abi value, unsigned* index, boolean* found) override
{
for (size_t i = 0; i < m_size; ++i)
{
if (m_data[i].Equals(value))
{
*index = static_cast<unsigned>(i);
*found = true;
return S_OK;
}
}
*index = 0;
*found = false;
return S_OK;
}
IFACEMETHODIMP SetAt(unsigned index, Abi item) override
{
if (index >= m_size)
{
return E_BOUNDS;
}
return m_data[index].Set(item);
}
IFACEMETHODIMP InsertAt(unsigned index, Abi item) override
{
// Insert at the end and swap it into place
if (index > m_size)
{
return E_BOUNDS;
}
auto hr = Append(item);
if (SUCCEEDED(hr))
{
for (size_t i = m_size - 1; i > index; --i)
{
wistd::swap_wil(m_data[i], m_data[i - 1]);
}
}
return hr;
}
IFACEMETHODIMP RemoveAt(unsigned index) override
{
if (index >= m_size)
{
return E_BOUNDS;
}
for (size_t i = index + 1; i < m_size; ++i)
{
wistd::swap_wil(m_data[i - 1], m_data[i]);
}
m_data[--m_size].Reset();
return S_OK;
}
IFACEMETHODIMP Append(Abi item) override
{
if (m_size > MaxSize)
{
return E_OUTOFMEMORY;
}
auto hr = m_data[m_size].Set(item);
if (SUCCEEDED(hr))
{
++m_size;
}
return hr;
}
IFACEMETHODIMP RemoveAtEnd() override
{
if (m_size == 0)
{
return E_BOUNDS;
}
m_data[--m_size].Reset();
return S_OK;
}
IFACEMETHODIMP Clear() override
{
for (size_t i = 0; i < m_size; ++i)
{
m_data[i].Reset();
}
m_size = 0;
return S_OK;
}
IFACEMETHODIMP GetMany(unsigned startIndex, unsigned capacity, Abi* value, unsigned* actual) override
{
*actual = 0;
if (startIndex >= m_size)
{
return S_OK;
}
auto count = m_size - startIndex;
count = (count > capacity) ? capacity : count;
HRESULT hr = S_OK;
unsigned i = 0;
for (; (i < count) && SUCCEEDED(hr); ++i)
{
hr = m_data[startIndex + i].CopyTo(value + i);
}
if (SUCCEEDED(hr))
{
*actual = static_cast<unsigned>(count);
}
else
{
while (i--)
{
WinRTStorage<Abi>::Destroy(value[i]);
}
}
return hr;
}
IFACEMETHODIMP ReplaceAll(unsigned count, Abi* value) override
{
if (count > MaxSize)
{
return E_OUTOFMEMORY;
}
Clear();
HRESULT hr = S_OK;
for (size_t i = 0; (i < count) && SUCCEEDED(hr); ++i)
{
hr = m_data[i].Set(value[i]);
}
if (FAILED(hr))
{
Clear();
}
return hr;
}
private:
size_t m_size = 0;
WinRTStorage<Abi> m_data[MaxSize];
};

460
Externals/WIL/tests/FileSystemTests.cpp vendored Normal file
View file

@ -0,0 +1,460 @@
#include <windows.h>
#include <winstring.h> // For wil::unique_hstring
#include <wil/common.h>
#ifdef WIL_ENABLE_EXCEPTIONS
#include <string>
#endif
// TODO: str_raw_ptr is not two-phase name lookup clean (https://github.com/Microsoft/wil/issues/8)
namespace wil
{
PCWSTR str_raw_ptr(HSTRING);
#ifdef WIL_ENABLE_EXCEPTIONS
PCWSTR str_raw_ptr(const std::wstring&);
#endif
}
#include <wil/filesystem.h>
#ifdef WIL_ENABLE_EXCEPTIONS
#include <wil/stl.h> // For std::wstring string_maker
#endif
#include "common.h"
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
bool DirectoryExists(_In_ PCWSTR path)
{
DWORD dwAttrib = GetFileAttributesW(path);
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}
TEST_CASE("FileSystemTests::CreateDirectory", "[filesystem]")
{
wchar_t basePath[MAX_PATH];
REQUIRE(GetTempPathW(ARRAYSIZE(basePath), basePath));
REQUIRE_SUCCEEDED(PathCchAppend(basePath, ARRAYSIZE(basePath), L"FileSystemTests"));
REQUIRE_FALSE(DirectoryExists(basePath));
REQUIRE(SUCCEEDED(wil::CreateDirectoryDeepNoThrow(basePath)));
REQUIRE(DirectoryExists(basePath));
auto scopeGuard = wil::scope_exit([&]
{
REQUIRE_SUCCEEDED(wil::RemoveDirectoryRecursiveNoThrow(basePath));
});
PCWSTR relativeTestPath = L"folder1\\folder2\\folder3\\folder4\\folder5\\folder6\\folder7\\folder8";
wchar_t absoluteTestPath[MAX_PATH];
REQUIRE_SUCCEEDED(StringCchCopyW(absoluteTestPath, ARRAYSIZE(absoluteTestPath), basePath));
REQUIRE_SUCCEEDED(PathCchAppend(absoluteTestPath, ARRAYSIZE(absoluteTestPath), relativeTestPath));
REQUIRE_FALSE(DirectoryExists(absoluteTestPath));
REQUIRE_SUCCEEDED(wil::CreateDirectoryDeepNoThrow(absoluteTestPath));
PCWSTR invalidCharsPath = L"Bad?Char|";
wchar_t absoluteInvalidPath[MAX_PATH];
REQUIRE_SUCCEEDED(StringCchCopyW(absoluteInvalidPath, ARRAYSIZE(absoluteInvalidPath), basePath));
REQUIRE_SUCCEEDED(PathCchAppend(absoluteInvalidPath, ARRAYSIZE(absoluteInvalidPath), invalidCharsPath));
REQUIRE_FALSE(DirectoryExists(absoluteInvalidPath));
REQUIRE_FALSE(SUCCEEDED(wil::CreateDirectoryDeepNoThrow(absoluteInvalidPath)));
PCWSTR testPath3 = L"folder1\\folder2\\folder3";
wchar_t absoluteTestPath3[MAX_PATH];
REQUIRE_SUCCEEDED(StringCchCopyW(absoluteTestPath3, ARRAYSIZE(absoluteTestPath3), basePath));
REQUIRE_SUCCEEDED(PathCchAppend(absoluteTestPath3, ARRAYSIZE(absoluteTestPath3), testPath3));
REQUIRE(DirectoryExists(absoluteTestPath3));
PCWSTR testPath4 = L"folder1\\folder2\\folder3\\folder4";
wchar_t absoluteTestPath4[MAX_PATH];
REQUIRE_SUCCEEDED(StringCchCopyW(absoluteTestPath4, ARRAYSIZE(absoluteTestPath4), basePath));
REQUIRE_SUCCEEDED(PathCchAppend(absoluteTestPath4, ARRAYSIZE(absoluteTestPath4), testPath4));
REQUIRE(DirectoryExists(absoluteTestPath4));
REQUIRE_SUCCEEDED(wil::RemoveDirectoryRecursiveNoThrow(absoluteTestPath3, wil::RemoveDirectoryOptions::KeepRootDirectory));
REQUIRE(DirectoryExists(absoluteTestPath3));
REQUIRE_FALSE(DirectoryExists(absoluteTestPath4));
}
#ifdef WIL_ENABLE_EXCEPTIONS
// Learn about the Win32 API normalization here: https://blogs.msdn.microsoft.com/jeremykuhne/2016/04/21/path-normalization/
// This test verifies the ability of RemoveDirectoryRecursive to be able to delete files
// that are in the non-normalized form.
TEST_CASE("FileSystemTests::VerifyRemoveDirectoryRecursiveCanDeleteFoldersWithNonNormalizedNames", "[filesystem]")
{
// Extended length paths can access files with non-normalized names.
// This function creates a path with that ability.
auto CreatePathThatCanAccessNonNormalizedNames = [](PCWSTR root, PCWSTR name)
{
wil::unique_hlocal_string path;
THROW_IF_FAILED(PathAllocCombine(root, name, PATHCCH_DO_NOT_NORMALIZE_SEGMENTS | PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH, &path));
REQUIRE(wil::is_extended_length_path(path.get()));
return path;
};
// Regular paths are normalized in the Win32 APIs thus can't address files in the non-normalized form.
// This function creates a regular path form but preserves the non-normalized parts of the input (for testing)
auto CreateRegularPath = [](PCWSTR root, PCWSTR name)
{
wil::unique_hlocal_string path;
THROW_IF_FAILED(PathAllocCombine(root, name, PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, &path));
REQUIRE_FALSE(wil::is_extended_length_path(path.get()));
return path;
};
struct TestCases
{
PCWSTR CreateWithName;
PCWSTR DeleteWithName;
wil::unique_hlocal_string (*CreatePathFunction)(PCWSTR root, PCWSTR name);
HRESULT ExpectedResult;
};
PCWSTR NormalizedName = L"Foo";
PCWSTR NonNormalizedName = L"Foo."; // The dot at the end is what makes this non-normalized.
const auto PathNotFoundError = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
TestCases tests[] =
{
{ NormalizedName, NormalizedName, CreateRegularPath, S_OK },
{ NonNormalizedName, NormalizedName, CreateRegularPath, PathNotFoundError },
{ NormalizedName, NonNormalizedName, CreateRegularPath, S_OK },
{ NonNormalizedName, NonNormalizedName, CreateRegularPath, PathNotFoundError },
{ NormalizedName, NormalizedName, CreatePathThatCanAccessNonNormalizedNames, S_OK },
{ NonNormalizedName, NormalizedName, CreatePathThatCanAccessNonNormalizedNames, PathNotFoundError },
{ NormalizedName, NonNormalizedName, CreatePathThatCanAccessNonNormalizedNames, PathNotFoundError },
{ NonNormalizedName, NonNormalizedName, CreatePathThatCanAccessNonNormalizedNames, S_OK },
};
auto folderRoot = wil::ExpandEnvironmentStringsW(LR"(%TEMP%)");
REQUIRE_FALSE(wil::is_extended_length_path(folderRoot.get()));
auto EnsureFolderWithNonCanonicalNameAndContentsExists = [&](const TestCases& test)
{
const auto enableNonNormalized = PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH | PATHCCH_DO_NOT_NORMALIZE_SEGMENTS;
wil::unique_hlocal_string targetFolder;
// Create a folder for testing using the extended length form to enable
// access to non-normalized forms of the path
THROW_IF_FAILED(PathAllocCombine(folderRoot.get(), test.CreateWithName, enableNonNormalized, &targetFolder));
// This ensures the folder is there and won't fail if it already exists (common when testing).
wil::CreateDirectoryDeep(targetFolder.get());
// Create a file in that folder with a non-normalized name (with the dot at the end).
wil::unique_hlocal_string extendedFilePath;
THROW_IF_FAILED(PathAllocCombine(targetFolder.get(), L"NonNormalized.", enableNonNormalized, &extendedFilePath));
wil::unique_hfile fileHandle(CreateFileW(extendedFilePath.get(), FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
THROW_LAST_ERROR_IF(!fileHandle);
};
for (auto const& test : tests)
{
// remove remnants from previous test that will cause failures
wil::RemoveDirectoryRecursiveNoThrow(CreatePathThatCanAccessNonNormalizedNames(folderRoot.get(), NormalizedName).get());
wil::RemoveDirectoryRecursiveNoThrow(CreatePathThatCanAccessNonNormalizedNames(folderRoot.get(), NonNormalizedName).get());
EnsureFolderWithNonCanonicalNameAndContentsExists(test);
auto deleteWithPath = test.CreatePathFunction(folderRoot.get(), test.DeleteWithName);
const auto hr = wil::RemoveDirectoryRecursiveNoThrow(deleteWithPath.get());
REQUIRE(test.ExpectedResult == hr);
}
}
#endif
// real paths to test
const wchar_t c_variablePath[] = L"%systemdrive%\\Windows\\System32\\Windows.Storage.dll";
const wchar_t c_expandedPath[] = L"c:\\Windows\\System32\\Windows.Storage.dll";
// // paths that should not exist on the system
const wchar_t c_missingVariable[] = L"%doesnotexist%\\doesnotexist.dll";
const wchar_t c_missingPath[] = L"c:\\Windows\\System32\\doesnotexist.dll";
const int c_stackBufferLimitTest = 5;
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("FileSystemTests::VerifyGetCurrentDirectory", "[filesystem]")
{
auto pwd = wil::GetCurrentDirectoryW();
REQUIRE(*pwd.get() != L'\0');
}
TEST_CASE("FileSystemTests::VerifyGetFullPathName", "[filesystem]")
{
PCWSTR fileName = L"ReadMe.txt";
auto result = wil::GetFullPathNameW<wil::unique_cotaskmem_string>(fileName, nullptr);
PCWSTR fileNameResult;
result = wil::GetFullPathNameW<wil::unique_cotaskmem_string>(fileName, &fileNameResult);
REQUIRE(wcscmp(fileName, fileNameResult) == 0);
auto result2 = wil::GetFullPathNameW<wil::unique_cotaskmem_string, c_stackBufferLimitTest>(fileName, &fileNameResult);
REQUIRE(wcscmp(fileName, fileNameResult) == 0);
REQUIRE(wcscmp(result.get(), result2.get()) == 0);
// The only negative test case I've found is a path > 32k.
std::wstring big(1024 * 32, L'a');
wil::unique_hstring output;
auto hr = wil::GetFullPathNameW(big.c_str(), output, nullptr);
REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE));
}
TEST_CASE("FileSystemTests::VerifyGetFinalPathNameByHandle", "[filesystem]")
{
wil::unique_hfile fileHandle(CreateFileW(c_expandedPath, FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, nullptr));
THROW_LAST_ERROR_IF(!fileHandle);
auto name = wil::GetFinalPathNameByHandleW(fileHandle.get());
auto name2 = wil::GetFinalPathNameByHandleW<wil::unique_cotaskmem_string, c_stackBufferLimitTest>(fileHandle.get());
REQUIRE(wcscmp(name.get(), name2.get()) == 0);
std::wstring path;
auto hr = wil::GetFinalPathNameByHandleW(nullptr, path);
REQUIRE(hr == E_HANDLE); // should be a usage error so be a fail fast.
// A more legitimate case is a non file handler like a drive volume.
wil::unique_hfile volumeHandle(CreateFileW(LR"(\\?\C:)", FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, nullptr));
THROW_LAST_ERROR_IF(!volumeHandle);
const auto hr2 = wil::GetFinalPathNameByHandleW(volumeHandle.get(), path);
REQUIRE(hr2 == HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION));
}
TEST_CASE("FileSystemTests::VerifyTrySearchPathW", "[filesystem]")
{
auto pathToTest = wil::TrySearchPathW(nullptr, c_expandedPath, nullptr);
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
pathToTest = wil::TrySearchPathW(nullptr, c_missingPath, nullptr);
REQUIRE(wil::string_get_not_null(pathToTest)[0] == L'\0');
}
#endif
// Simple test to expand an environmental string
TEST_CASE("FileSystemTests::VerifyExpandEnvironmentStringsW", "[filesystem]")
{
wil::unique_cotaskmem_string pathToTest;
REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(c_variablePath, pathToTest));
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
// This should effectively be a no-op
REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(c_expandedPath, pathToTest));
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
// Environment variable does not exist, but the call should still succeed
REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(c_missingVariable, pathToTest));
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_missingVariable, -1, TRUE) == CSTR_EQUAL);
}
TEST_CASE("FileSystemTests::VerifySearchPathW", "[filesystem]")
{
wil::unique_cotaskmem_string pathToTest;
REQUIRE_SUCCEEDED(wil::SearchPathW(nullptr, c_expandedPath, nullptr, pathToTest));
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
REQUIRE(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == wil::SearchPathW(nullptr, c_missingPath, nullptr, pathToTest));
}
TEST_CASE("FileSystemTests::VerifyExpandEnvAndSearchPath", "[filesystem]")
{
wil::unique_cotaskmem_string pathToTest;
REQUIRE_SUCCEEDED(wil::ExpandEnvAndSearchPath(c_variablePath, pathToTest));
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
// This test will exercise the case where AdaptFixedSizeToAllocatedResult will need to
// reallocate the initial buffer to fit the final string.
// This test is sufficient to test both wil::ExpandEnvironmentStringsW and wil::SeachPathW
REQUIRE_SUCCEEDED((wil::ExpandEnvAndSearchPath<wil::unique_cotaskmem_string, c_stackBufferLimitTest>(c_variablePath, pathToTest)));
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
pathToTest.reset();
REQUIRE(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == wil::ExpandEnvAndSearchPath(c_missingVariable, pathToTest));
REQUIRE(pathToTest.get() == nullptr);
}
TEST_CASE("FileSystemTests::VerifyGetSystemDirectoryW", "[filesystem]")
{
wil::unique_cotaskmem_string pathToTest;
REQUIRE_SUCCEEDED(wil::GetSystemDirectoryW(pathToTest));
// allocate based on the string that wil::GetSystemDirectoryW returned
size_t length = wcslen(pathToTest.get()) + 1;
auto trueSystemDir = wil::make_cotaskmem_string_nothrow(nullptr, length);
REQUIRE(GetSystemDirectoryW(trueSystemDir.get(), static_cast<UINT>(length)) > 0);
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, trueSystemDir.get(), -1, TRUE) == CSTR_EQUAL);
// Force AdaptFixed* to realloc. Test stack boundary with small initial buffer limit, c_stackBufferLimitTest
REQUIRE_SUCCEEDED((wil::GetSystemDirectoryW<wil::unique_cotaskmem_string, c_stackBufferLimitTest>(pathToTest)));
// allocate based on the string that wil::GetSystemDirectoryW returned
length = wcslen(pathToTest.get()) + 1;
trueSystemDir = wil::make_cotaskmem_string_nothrow(nullptr, length);
REQUIRE(GetSystemDirectoryW(trueSystemDir.get(), static_cast<UINT>(length)) > 0);
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, trueSystemDir.get(), -1, TRUE) == CSTR_EQUAL);
}
struct has_operator_pcwstr
{
PCWSTR value;
operator PCWSTR() const
{
return value;
}
};
struct has_operator_pwstr
{
PWSTR value;
operator PWSTR() const
{
return value;
}
};
#ifdef WIL_ENABLE_EXCEPTIONS
struct has_operator_wstr_ref
{
std::wstring value;
operator const std::wstring&() const
{
return value;
}
};
// E.g. mimics something like std::filesystem::path
struct has_operator_wstr
{
std::wstring value;
operator std::wstring() const
{
return value;
}
};
#endif
TEST_CASE("FileSystemTests::VerifyStrConcat", "[filesystem]")
{
SECTION("Concat with multiple strings")
{
PCWSTR test1 = L"Test1";
#ifdef WIL_ENABLE_EXCEPTIONS
std::wstring test2 = L"Test2";
#else
PCWSTR test2 = L"Test2";
#endif
WCHAR test3[6] = L"Test3";
wil::unique_cotaskmem_string test4 = wil::make_unique_string_nothrow<wil::unique_cotaskmem_string>(L"test4");
wil::unique_hstring test5 = wil::make_unique_string_nothrow<wil::unique_hstring>(L"test5");
has_operator_pcwstr test6{ L"Test6" };
WCHAR test7Buffer[] = L"Test7";
has_operator_pwstr test7{ test7Buffer };
#ifdef WIL_ENABLE_EXCEPTIONS
has_operator_wstr_ref test8{ L"Test8" };
has_operator_wstr test9{ L"Test9" };
#else
PCWSTR test8 = L"Test8";
PCWSTR test9 = L"Test9";
#endif
PCWSTR expectedStr = L"Test1Test2Test3Test4Test5Test6Test7Test8Test9";
#ifdef WIL_ENABLE_EXCEPTIONS
auto combinedString = wil::str_concat<wil::unique_cotaskmem_string>(test1, test2, test3, test4, test5, test6, test7, test8, test9);
REQUIRE(CompareStringOrdinal(combinedString.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL);
#endif
wil::unique_cotaskmem_string combinedStringNT;
REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, test1, test2, test3, test4, test5, test6, test7, test8, test9));
REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL);
auto combinedStringFF = wil::str_concat_failfast<wil::unique_cotaskmem_string>(test1, test2, test3, test4, test5, test6, test7, test8, test9);
REQUIRE(CompareStringOrdinal(combinedStringFF.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL);
}
SECTION("Concat with single string")
{
PCWSTR test1 = L"Test1";
#ifdef WIL_ENABLE_EXCEPTIONS
auto combinedString = wil::str_concat<wil::unique_cotaskmem_string>(test1);
REQUIRE(CompareStringOrdinal(combinedString.get(), -1, test1, -1, TRUE) == CSTR_EQUAL);
#endif
wil::unique_cotaskmem_string combinedStringNT;
REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, test1));
REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, test1, -1, TRUE) == CSTR_EQUAL);
auto combinedStringFF = wil::str_concat_failfast<wil::unique_cotaskmem_string>(test1);
REQUIRE(CompareStringOrdinal(combinedStringFF.get(), -1, test1, -1, TRUE) == CSTR_EQUAL);
}
SECTION("Concat with existing string")
{
std::wstring test2 = L"Test2";
WCHAR test3[6] = L"Test3";
PCWSTR expectedStr = L"Test1Test2Test3";
wil::unique_cotaskmem_string combinedStringNT = wil::make_unique_string_nothrow<wil::unique_cotaskmem_string>(L"Test1");
REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, test2.c_str(), test3));
REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL);
}
}
TEST_CASE("FileSystemTests::VerifyStrPrintf", "[filesystem]")
{
#ifdef WIL_ENABLE_EXCEPTIONS
auto formattedString = wil::str_printf<wil::unique_cotaskmem_string>(L"Test %s %c %d %4.2f", L"String", L'c', 42, 6.28);
REQUIRE(CompareStringOrdinal(formattedString.get(), -1, L"Test String c 42 6.28", -1, TRUE) == CSTR_EQUAL);
#endif
wil::unique_cotaskmem_string formattedStringNT;
REQUIRE_SUCCEEDED(wil::str_printf_nothrow(formattedStringNT, L"Test %s %c %d %4.2f", L"String", L'c', 42, 6.28));
REQUIRE(CompareStringOrdinal(formattedStringNT.get(), -1, L"Test String c 42 6.28", -1, TRUE) == CSTR_EQUAL);
auto formattedStringFF = wil::str_printf_failfast<wil::unique_cotaskmem_string>(L"Test %s %c %d %4.2f", L"String", L'c', 42, 6.28);
REQUIRE(CompareStringOrdinal(formattedStringFF.get(), -1, L"Test String c 42 6.28", -1, TRUE) == CSTR_EQUAL);
}
TEST_CASE("FileSystemTests::VerifyGetModuleFileNameW", "[filesystem]")
{
wil::unique_cotaskmem_string path;
REQUIRE_SUCCEEDED(wil::GetModuleFileNameW(nullptr, path));
auto len = wcslen(path.get());
REQUIRE(((len >= 4) && (wcscmp(path.get() + len - 4, L".exe") == 0)));
// Call again, but force multiple retries through a small initial buffer
wil::unique_cotaskmem_string path2;
REQUIRE_SUCCEEDED((wil::GetModuleFileNameW<wil::unique_cotaskmem_string, 4>(nullptr, path2)));
REQUIRE(wcscmp(path.get(), path2.get()) == 0);
REQUIRE_FAILED(wil::GetModuleFileNameW((HMODULE)INVALID_HANDLE_VALUE, path));
}
TEST_CASE("FileSystemTests::VerifyGetModuleFileNameExW", "[filesystem]")
{
wil::unique_cotaskmem_string path;
REQUIRE_SUCCEEDED(wil::GetModuleFileNameExW(nullptr, nullptr, path));
auto len = wcslen(path.get());
REQUIRE(((len >= 4) && (wcscmp(path.get() + len - 4, L".exe") == 0)));
// Call again, but force multiple retries through a small initial buffer
wil::unique_cotaskmem_string path2;
REQUIRE_SUCCEEDED((wil::GetModuleFileNameExW<wil::unique_cotaskmem_string, 4>(nullptr, nullptr, path2)));
REQUIRE(wcscmp(path.get(), path2.get()) == 0);
REQUIRE_FAILED(wil::GetModuleFileNameExW(nullptr, (HMODULE)INVALID_HANDLE_VALUE, path));
}
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)

145
Externals/WIL/tests/MallocSpy.h vendored Normal file
View file

@ -0,0 +1,145 @@
#pragma once
#include "catch.hpp"
#include <objbase.h>
#include <wil/wistd_functional.h>
#include <wrl/implements.h>
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
// IMallocSpy requires you to implement all methods, but we often only want one or two...
struct MallocSpy : Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IMallocSpy>
{
wistd::function<SIZE_T(SIZE_T)> PreAllocCallback;
virtual SIZE_T STDMETHODCALLTYPE PreAlloc(SIZE_T requestSize) override
{
if (PreAllocCallback)
{
return PreAllocCallback(requestSize);
}
return requestSize;
}
wistd::function<void*(void*)> PostAllocCallback;
virtual void* STDMETHODCALLTYPE PostAlloc(void* ptr) override
{
if (PostAllocCallback)
{
return PostAllocCallback(ptr);
}
return ptr;
}
wistd::function<void*(void*)> PreFreeCallback;
virtual void* STDMETHODCALLTYPE PreFree(void* ptr, BOOL wasSpyed) override
{
if (wasSpyed && PreFreeCallback)
{
return PreFreeCallback(ptr);
}
return ptr;
}
virtual void STDMETHODCALLTYPE PostFree(BOOL /*wasSpyed*/) override
{
}
wistd::function<SIZE_T(void*, SIZE_T, void**)> PreReallocCallback;
virtual SIZE_T STDMETHODCALLTYPE PreRealloc(void* ptr, SIZE_T requestSize, void** newPtr, BOOL wasSpyed) override
{
*newPtr = ptr;
if (wasSpyed && PreReallocCallback)
{
return PreReallocCallback(ptr, requestSize, newPtr);
}
return requestSize;
}
wistd::function<void*(void*)> PostReallocCallback;
virtual void* STDMETHODCALLTYPE PostRealloc(void* ptr, BOOL wasSpyed) override
{
if (wasSpyed && PostReallocCallback)
{
return PostReallocCallback(ptr);
}
return ptr;
}
wistd::function<void*(void*)> PreGetSizeCallback;
virtual void* STDMETHODCALLTYPE PreGetSize(void* ptr, BOOL wasSpyed) override
{
if (wasSpyed && PreGetSizeCallback)
{
return PreGetSizeCallback(ptr);
}
return ptr;
}
wistd::function<SIZE_T(SIZE_T)> PostGetSizeCallback;
virtual SIZE_T STDMETHODCALLTYPE PostGetSize(SIZE_T size, BOOL wasSpyed) override
{
if (wasSpyed && PostGetSizeCallback)
{
return PostGetSizeCallback(size);
}
return size;
}
wistd::function<void*(void*)> PreDidAllocCallback;
virtual void* STDMETHODCALLTYPE PreDidAlloc(void* ptr, BOOL wasSpyed) override
{
if (wasSpyed && PreDidAllocCallback)
{
return PreDidAllocCallback(ptr);
}
return ptr;
}
virtual int STDMETHODCALLTYPE PostDidAlloc(void* /*ptr*/, BOOL /*wasSpyed*/, int result) override
{
return result;
}
virtual void STDMETHODCALLTYPE PreHeapMinimize() override
{
}
virtual void STDMETHODCALLTYPE PostHeapMinimize() override
{
}
};
Microsoft::WRL::ComPtr<MallocSpy> MakeSecureDeleterMallocSpy()
{
using namespace Microsoft::WRL;
auto result = Make<MallocSpy>();
REQUIRE(result);
result->PreFreeCallback = [](void* ptr)
{
ComPtr<IMalloc> malloc;
if (SUCCEEDED(::CoGetMalloc(1, &malloc)))
{
auto size = malloc->GetSize(ptr);
auto buffer = static_cast<byte*>(ptr);
for (size_t i = 0; i < size; ++i)
{
REQUIRE(buffer[i] == 0);
}
}
return ptr;
};
return result;
}
#endif

735
Externals/WIL/tests/ResourceTests.cpp vendored Normal file
View file

@ -0,0 +1,735 @@
// Included first and then again later to ensure that we're able to "light up" new functionality based off new includes
#include <wil/resource.h>
#include <wil/com.h>
#include <wil/stl.h>
// Headers to "light up" functionality in resource.h
#include <memory>
#include <roapi.h>
#include <winstring.h>
#include <wil/resource.h>
#include <wrl/implements.h>
#include "common.h"
TEST_CASE("ResourceTests::TestLastErrorContext", "[resource][last_error_context]")
{
// Destructing the last_error_context restores the error.
{
SetLastError(42);
auto error42 = wil::last_error_context();
SetLastError(0);
}
REQUIRE(GetLastError() == 42);
// The context can be moved.
{
SetLastError(42);
auto error42 = wil::last_error_context();
SetLastError(0);
{
auto another_error42 = wil::last_error_context(std::move(error42));
SetLastError(1);
}
REQUIRE(GetLastError() == 42);
SetLastError(0);
// error42 has been moved-from and should not do anything at destruction.
}
REQUIRE(GetLastError() == 0);
// The context can be self-assigned, which has no effect.
{
SetLastError(42);
auto error42 = wil::last_error_context();
SetLastError(0);
error42 = std::move(error42);
SetLastError(1);
}
REQUIRE(GetLastError() == 42);
// The context can be dismissed, which cause it to do nothing at destruction.
{
SetLastError(42);
auto error42 = wil::last_error_context();
SetLastError(0);
error42.release();
SetLastError(1);
}
REQUIRE(GetLastError() == 1);
}
TEST_CASE("ResourceTests::TestScopeExit", "[resource][scope_exit]")
{
int count = 0;
auto validate = [&](int expected) { REQUIRE(count == expected); count = 0; };
{
auto foo = wil::scope_exit([&] { count++; });
}
validate(1);
{
auto foo = wil::scope_exit([&] { count++; });
foo.release();
foo.reset();
}
validate(0);
{
auto foo = wil::scope_exit([&] { count++; });
foo.reset();
foo.reset();
validate(1);
}
validate(0);
#ifdef WIL_ENABLE_EXCEPTIONS
{
auto foo = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { count++; THROW_HR(E_FAIL); });
}
validate(1);
{
auto foo = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { count++; THROW_HR(E_FAIL); });
foo.release();
foo.reset();
}
validate(0);
{
auto foo = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { count++; THROW_HR(E_FAIL); });
foo.reset();
foo.reset();
validate(1);
}
validate(0);
#endif // WIL_ENABLE_EXCEPTIONS
}
interface __declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20b00"))
ITest : public IUnknown
{
STDMETHOD_(void, Test)() = 0;
};
class PointerTestObject : witest::AllocatedObject,
public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>, ITest>
{
public:
STDMETHOD_(void, Test)() {};
};
TEST_CASE("ResourceTests::TestOperationsOnGenericSmartPointerClasses", "[resource]")
{
#ifdef WIL_ENABLE_EXCEPTIONS
{
// wil::unique_any_t example
wil::unique_event ptr2(wil::EventOptions::ManualReset);
// wil::com_ptr
wil::com_ptr<PointerTestObject> ptr3 = Microsoft::WRL::Make<PointerTestObject>();
// wil::shared_any_t example
wil::shared_event ptr4(wil::EventOptions::ManualReset);
// wistd::unique_ptr example
auto ptr5 = wil::make_unique_failfast<POINT>();
static_assert(wistd::is_same<typename wil::smart_pointer_details<decltype(ptr2)>::pointer, HANDLE>::value, "type-mismatch");
static_assert(wistd::is_same<typename wil::smart_pointer_details<decltype(ptr3)>::pointer, PointerTestObject*>::value, "type-mismatch");
auto p2 = wil::detach_from_smart_pointer(ptr2);
auto p3 = wil::detach_from_smart_pointer(ptr3);
// auto p4 = wil::detach_from_smart_pointer(ptr4); // wil::shared_any_t and std::shared_ptr do not support release().
HANDLE p4{};
auto p5 = wil::detach_from_smart_pointer(ptr5);
REQUIRE((!ptr2 && !ptr3));
REQUIRE((p2 && p3));
wil::attach_to_smart_pointer(ptr2, p2);
wil::attach_to_smart_pointer(ptr3, p3);
wil::attach_to_smart_pointer(ptr4, p4);
wil::attach_to_smart_pointer(ptr5, p5);
p2 = nullptr;
p3 = nullptr;
p4 = nullptr;
p5 = nullptr;
wil::detach_to_opt_param(&p2, ptr2);
wil::detach_to_opt_param(&p3, ptr3);
REQUIRE((!ptr2 && !ptr3));
REQUIRE((p2 && p3));
wil::attach_to_smart_pointer(ptr2, p2);
wil::attach_to_smart_pointer(ptr3, p3);
p2 = nullptr;
p3 = nullptr;
wil::detach_to_opt_param(&p2, ptr2);
wil::detach_to_opt_param(&p3, ptr3);
REQUIRE((!ptr2 && !ptr3));
REQUIRE((p2 && p3));
[&](decltype(p2)* ptr) { *ptr = p2; } (wil::out_param(ptr2));
[&](decltype(p3)* ptr) { *ptr = p3; } (wil::out_param(ptr3));
[&](decltype(p4)* ptr) { *ptr = p4; } (wil::out_param(ptr4));
[&](decltype(p5)* ptr) { *ptr = p5; } (wil::out_param(ptr5));
REQUIRE((ptr2 && ptr3));
// Validate R-Value compilation
wil::detach_to_opt_param(&p2, decltype(ptr2){});
wil::detach_to_opt_param(&p3, decltype(ptr3){});
}
#endif
std::unique_ptr<int> ptr1(new int(1));
Microsoft::WRL::ComPtr<PointerTestObject> ptr4 = Microsoft::WRL::Make<PointerTestObject>();
static_assert(wistd::is_same<typename wil::smart_pointer_details<decltype(ptr1)>::pointer, int*>::value, "type-mismatch");
static_assert(wistd::is_same<typename wil::smart_pointer_details<decltype(ptr4)>::pointer, PointerTestObject*>::value, "type-mismatch");
auto p1 = wil::detach_from_smart_pointer(ptr1);
auto p4 = wil::detach_from_smart_pointer(ptr4);
REQUIRE((!ptr1 && !ptr4));
REQUIRE((p1 && p4));
wil::attach_to_smart_pointer(ptr1, p1);
wil::attach_to_smart_pointer(ptr4, p4);
REQUIRE((ptr1 && ptr4));
p1 = nullptr;
p4 = nullptr;
int** pNull = nullptr;
wil::detach_to_opt_param(pNull, ptr1);
REQUIRE(ptr1);
wil::detach_to_opt_param(&p1, ptr1);
wil::detach_to_opt_param(&p4, ptr4);
REQUIRE((!ptr1 && !ptr4));
REQUIRE((p1 && p4));
[&](decltype(p1)* ptr) { *ptr = p1; } (wil::out_param(ptr1));
[&](decltype(p4)* ptr) { *ptr = p4; } (wil::out_param(ptr4));
REQUIRE((ptr1 && ptr4));
p1 = wil::detach_from_smart_pointer(ptr1);
[&](int** ptr) { *ptr = p1; } (wil::out_param_ptr<int **>(ptr1));
REQUIRE(ptr1);
}
// Compilation only test...
void StlAdlTest()
{
// This test has exposed some Argument Dependent Lookup issues in wistd / stl. Primarily we're
// just looking for clean compilation.
std::vector<wistd::unique_ptr<int>> v;
v.emplace_back(new int{ 1 });
v.emplace_back(new int{ 2 });
v.emplace_back(new int{ 3 });
std::rotate(begin(v), begin(v) + 1, end(v));
REQUIRE(*v[0] == 1);
REQUIRE(*v[1] == 3);
REQUIRE(*v[2] == 2);
decltype(v) v2;
v2 = std::move(v);
REQUIRE(*v2[0] == 1);
REQUIRE(*v2[1] == 3);
REQUIRE(*v2[2] == 2);
decltype(v) v3;
std::swap(v2, v3);
REQUIRE(*v3[0] == 1);
REQUIRE(*v3[1] == 3);
REQUIRE(*v3[2] == 2);
}
// Compilation only test...
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
void UniqueProcessInfo()
{
wil::unique_process_information process;
CreateProcessW(nullptr, nullptr, nullptr, nullptr, FALSE, 0, nullptr, nullptr, nullptr, &process);
ResumeThread(process.hThread);
WaitForSingleObject(process.hProcess, INFINITE);
wil::unique_process_information other(wistd::move(process));
}
#endif
struct FakeComInterface
{
void AddRef()
{
refs++;
}
void Release()
{
refs--;
}
HRESULT __stdcall Close()
{
closes++;
return S_OK;
}
size_t refs = 0;
size_t closes = 0;
bool called()
{
auto old = closes;
closes = 0;
return (old > 0);
}
bool has_ref()
{
return (refs > 0);
}
};
static void __stdcall CloseFakeComInterface(FakeComInterface* fake)
{
fake->Close();
}
using unique_fakeclose_call = wil::unique_com_call<FakeComInterface, decltype(&CloseFakeComInterface), CloseFakeComInterface>;
TEST_CASE("ResourceTests::VerifyUniqueComCall", "[resource][unique_com_call]")
{
unique_fakeclose_call call1;
unique_fakeclose_call call2;
// intentional compilation errors
// unique_fakeclose_call call3 = call1;
// call2 = call1;
FakeComInterface fake1;
unique_fakeclose_call call4(&fake1);
REQUIRE(fake1.has_ref());
unique_fakeclose_call call5(wistd::move(call4));
REQUIRE(!call4);
REQUIRE(call5);
REQUIRE(fake1.has_ref());
call4 = wistd::move(call5);
REQUIRE(call4);
REQUIRE(!call5);
REQUIRE(fake1.has_ref());
REQUIRE(!fake1.called());
FakeComInterface fake2;
{
unique_fakeclose_call scoped(&fake2);
}
REQUIRE(!fake2.has_ref());
REQUIRE(fake2.called());
call4.reset(&fake2);
REQUIRE(fake1.called());
REQUIRE(!fake1.has_ref());
call4.reset();
REQUIRE(!fake2.has_ref());
REQUIRE(fake2.called());
call1.reset(&fake1);
call2.swap(call1);
REQUIRE((call2 && !call1));
call2.release();
REQUIRE(!fake1.called());
REQUIRE(!fake1.has_ref());
REQUIRE(!call2);
REQUIRE(*call1.addressof() == nullptr);
call1.reset(&fake1);
fake2.closes = 0;
fake2.refs = 1;
*(&call1) = &fake2;
REQUIRE(!fake1.has_ref());
REQUIRE(fake1.called());
REQUIRE(fake2.has_ref());
call1.reset(&fake1);
fake2.closes = 0;
fake2.refs = 1;
*call1.put() = &fake2;
REQUIRE(!fake1.has_ref());
REQUIRE(fake1.called());
REQUIRE(fake2.has_ref());
call1.reset();
REQUIRE(!fake2.has_ref());
REQUIRE(fake2.called());
}
static bool g_called = false;
static bool called()
{
auto call = g_called;
g_called = false;
return (call);
}
static void __stdcall FakeCall()
{
g_called = true;
}
using unique_fake_call = wil::unique_call<decltype(&FakeCall), FakeCall>;
TEST_CASE("ResourceTests::VerifyUniqueCall", "[resource][unique_call]")
{
unique_fake_call call1;
unique_fake_call call2;
// intentional compilation errors
// unique_fake_call call3 = call1;
// call2 = call1;
unique_fake_call call4;
REQUIRE(!called());
unique_fake_call call5(wistd::move(call4));
REQUIRE(!call4);
REQUIRE(call5);
call4 = wistd::move(call5);
REQUIRE(call4);
REQUIRE(!call5);
REQUIRE(!called());
{
unique_fake_call scoped;
}
REQUIRE(called());
call4.reset();
REQUIRE(called());
call4.reset();
REQUIRE(!called());
call1.release();
REQUIRE((!call1 && call2));
call2.swap(call1);
REQUIRE((call1 && !call2));
call2.release();
REQUIRE(!called());
REQUIRE(!call2);
#ifdef __WIL__ROAPI_H_APPEXCEPTIONAL
{
auto call = wil::RoInitialize();
}
#endif
#ifdef __WIL__ROAPI_H_APP
{
wil::unique_rouninitialize_call uninit;
uninit.release();
auto call = wil::RoInitialize_failfast();
}
#endif
#ifdef __WIL__COMBASEAPI_H_APPEXCEPTIONAL
{
auto call = wil::CoInitializeEx();
}
#endif
#ifdef __WIL__COMBASEAPI_H_APP
{
wil::unique_couninitialize_call uninit;
uninit.release();
auto call = wil::CoInitializeEx_failfast();
}
#endif
}
void UniqueCallCompilationTest()
{
#ifdef __WIL__COMBASEAPI_H_EXCEPTIONAL
{
auto call = wil::CoImpersonateClient();
}
#endif
#ifdef __WIL__COMBASEAPI_H_
{
wil::unique_coreverttoself_call uninit;
uninit.release();
auto call = wil::CoImpersonateClient_failfast();
}
#endif
}
template<typename StringType, typename VerifyContents>
static void TestStringMaker(VerifyContents&& verifyContents)
{
PCWSTR values[] =
{
L"",
L"value",
// 300 chars
L"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
L"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
L"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
};
for (const auto& value : values)
{
auto const valueLength = wcslen(value);
// Direct construction case.
wil::details::string_maker<StringType> maker;
THROW_IF_FAILED(maker.make(value, valueLength));
auto result = maker.release();
verifyContents(value, valueLength, result);
// Two phase construction case.
THROW_IF_FAILED(maker.make(nullptr, valueLength));
REQUIRE(maker.buffer() != nullptr);
// In the case of the wil::unique_hstring and the empty string the buffer is in a read only
// section and can't be written to, so StringCchCopy(maker.buffer(), valueLength + 1, value) will fault adding the nul terminator.
// Use memcpy_s specifying exact size that will be zero in this case instead.
memcpy_s(maker.buffer(), valueLength * sizeof(*value), value, valueLength * sizeof(*value));
result = maker.release();
verifyContents(value, valueLength, result);
{
// no promote, ensure no leaks (not tested here, inspect in the debugger)
wil::details::string_maker<StringType> maker2;
THROW_IF_FAILED(maker2.make(value, valueLength));
}
}
}
#ifdef WIL_ENABLE_EXCEPTIONS
template <typename StringType>
static void VerifyMakeUniqueString(bool nullValueSupported = true)
{
if (nullValueSupported)
{
auto value0 = wil::make_unique_string<StringType>(nullptr, 5);
}
struct
{
PCWSTR expectedValue;
PCWSTR testValue;
// this is an optional parameter
size_t testLength = static_cast<size_t>(-1);
}
const testCaseEntries[] =
{
{ L"value", L"value", 5 },
{ L"value", L"value" },
{ L"va", L"va\0ue", 5 },
{ L"v", L"value", 1 },
{ L"\0", L"", 5 },
{ L"\0", nullptr, 5 },
};
using maker = wil::details::string_maker<StringType>;
for (auto const &entry : testCaseEntries)
{
bool shouldSkipNullString = ((wcscmp(entry.expectedValue, L"\0") == 0) && !nullValueSupported);
if (!shouldSkipNullString)
{
auto desiredValue = wil::make_unique_string<StringType>(entry.expectedValue);
auto stringValue = wil::make_unique_string<StringType>(entry.testValue, entry.testLength);
auto stringValueNoThrow = wil::make_unique_string_nothrow<StringType>(entry.testValue, entry.testLength);
auto stringValueFailFast = wil::make_unique_string_failfast<StringType>(entry.testValue, entry.testLength);
REQUIRE(wcscmp(maker::get(desiredValue), maker::get(stringValue)) == 0);
REQUIRE(wcscmp(maker::get(desiredValue), maker::get(stringValueNoThrow)) == 0);
REQUIRE(wcscmp(maker::get(desiredValue), maker::get(stringValueFailFast)) == 0);
}
}
}
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerCoTaskMem", "[resource][string_maker]")
{
VerifyMakeUniqueString<wil::unique_cotaskmem_string>();
TestStringMaker<wil::unique_cotaskmem_string>(
[](PCWSTR value, size_t /*valueLength*/, const wil::unique_cotaskmem_string& result)
{
REQUIRE(wcscmp(value, result.get()) == 0);
});
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerLocalAlloc", "[resource][string_maker]")
{
VerifyMakeUniqueString<wil::unique_hlocal_string>();
TestStringMaker<wil::unique_hlocal_string>(
[](PCWSTR value, size_t /*valueLength*/, const wil::unique_hlocal_string& result)
{
REQUIRE(wcscmp(value, result.get()) == 0);
});
}
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerGlobalAlloc", "[resource][string_maker]")
{
VerifyMakeUniqueString<wil::unique_hglobal_string>();
TestStringMaker<wil::unique_hglobal_string>(
[](PCWSTR value, size_t /*valueLength*/, const wil::unique_hglobal_string& result)
{
REQUIRE(wcscmp(value, result.get()) == 0);
});
}
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerProcessHeap", "[resource][string_maker]")
{
VerifyMakeUniqueString<wil::unique_process_heap_string>();
TestStringMaker<wil::unique_process_heap_string>(
[](PCWSTR value, size_t /*valueLength*/, const wil::unique_process_heap_string& result)
{
REQUIRE(wcscmp(value, result.get()) == 0);
});
}
#endif
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerMidl", "[resource][string_maker]")
{
VerifyMakeUniqueString<wil::unique_midl_string>();
TestStringMaker<wil::unique_midl_string>(
[](PCWSTR value, size_t /*valueLength*/, const wil::unique_midl_string& result)
{
REQUIRE(wcscmp(value, result.get()) == 0);
});
}
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerHString", "[resource][string_maker]")
{
wil::unique_hstring value;
value.reset(static_cast<HSTRING>(nullptr));
VerifyMakeUniqueString<wil::unique_hstring>(false);
TestStringMaker<wil::unique_hstring>(
[](PCWSTR value, size_t valueLength, const wil::unique_hstring& result)
{
UINT32 length;
REQUIRE(wcscmp(value, WindowsGetStringRawBuffer(result.get(), &length)) == 0);
REQUIRE(valueLength == length);
});
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerStdWString", "[resource][string_maker]")
{
std::string s;
wil::details::string_maker<std::wstring> maker;
TestStringMaker<std::wstring>(
[](PCWSTR value, size_t valueLength, const std::wstring& result)
{
REQUIRE(wcscmp(value, result.c_str()) == 0);
REQUIRE(result == value);
REQUIRE(result.size() == valueLength);
});
}
#endif
TEST_CASE("UniqueStringAndStringMakerTests::VerifyLegacySTringMakers", "[resource][string_maker]")
{
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
auto l = wil::make_hlocal_string(L"value");
l = wil::make_hlocal_string_nothrow(L"value");
l = wil::make_hlocal_string_failfast(L"value");
auto p = wil::make_process_heap_string(L"value");
p = wil::make_process_heap_string_nothrow(L"value");
p = wil::make_process_heap_string_failfast(L"value");
#endif
auto c = wil::make_cotaskmem_string(L"value");
c = wil::make_cotaskmem_string_nothrow(L"value");
c = wil::make_cotaskmem_string_failfast(L"value");
}
#endif
_Use_decl_annotations_ void* __RPC_USER MIDL_user_allocate(size_t size)
{
return ::HeapAlloc(GetProcessHeap(), 0, size);
}
_Use_decl_annotations_ void __RPC_USER MIDL_user_free(void* p)
{
::HeapFree(GetProcessHeap(), 0, p);
}
TEST_CASE("UniqueMidlStringTests", "[resource][rpc]")
{
wil::unique_midl_ptr<int[]> intArray{ reinterpret_cast<int*>(::MIDL_user_allocate(sizeof(int) * 10)) };
intArray[2] = 1;
wil::unique_midl_ptr<int> intSingle{ reinterpret_cast<int*>(::MIDL_user_allocate(sizeof(int) * 1)) };
}
TEST_CASE("UniqueEnvironmentStrings", "[resource][win32]")
{
wil::unique_environstrings_ptr env{ ::GetEnvironmentStringsW() };
const wchar_t* nextVar = env.get();
while (nextVar &&* nextVar)
{
// consume 'nextVar'
nextVar += wcslen(nextVar) + 1;
}
wil::unique_environansistrings_ptr envAnsi{ ::GetEnvironmentStringsA() };
const char* nextVarAnsi = envAnsi.get();
while (nextVarAnsi && *nextVarAnsi)
{
// consume 'nextVar'
nextVarAnsi += strlen(nextVarAnsi) + 1;
}
}
TEST_CASE("UniqueVariant", "[resource][com]")
{
wil::unique_variant var;
var.vt = VT_BSTR;
var.bstrVal = ::SysAllocString(L"25");
REQUIRE(var.bstrVal != nullptr);
auto call = [](const VARIANT&) {};
call(var);
VARIANT weakVar = var;
(void)weakVar;
wil::unique_variant var2;
REQUIRE_SUCCEEDED(VariantChangeType(&var2, &var, 0, VT_UI4));
REQUIRE(var2.vt == VT_UI4);
REQUIRE(var2.uiVal == 25);
}
TEST_CASE("DefaultTemplateParamCompiles", "[resource]")
{
wil::unique_process_heap_ptr<> a;
wil::unique_virtualalloc_ptr<> b;
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
wil::unique_hlocal_ptr<> c;
wil::unique_hlocal_secure_ptr<> d;
wil::unique_hglobal_ptr<> e;
wil::unique_cotaskmem_secure_ptr<> f;
#endif
wil::unique_midl_ptr<> g;
wil::unique_cotaskmem_ptr<> h;
}

575
Externals/WIL/tests/ResultTests.cpp vendored Normal file
View file

@ -0,0 +1,575 @@
#include <wil/com.h>
#include <wil/result.h>
#include <wil/result_originate.h>
#include <roerrorapi.h>
#include "common.h"
static volatile long objectCount = 0;
struct SharedObject
{
SharedObject()
{
::InterlockedIncrement(&objectCount);
}
~SharedObject()
{
::InterlockedDecrement(&objectCount);
}
void ProcessShutdown()
{
}
int value{};
};
TEST_CASE("ResultTests::SemaphoreValue", "[result]")
{
auto TestValue = [&](auto start, auto end)
{
wil::details_abi::SemaphoreValue semaphore;
for (auto index = start; index <= end; index++)
{
semaphore.Destroy();
REQUIRE(SUCCEEDED(semaphore.CreateFromValue(L"test", index)));
auto num1 = index;
auto num2 = index;
REQUIRE(SUCCEEDED(semaphore.TryGetValue(L"test", &num1)));
REQUIRE(SUCCEEDED(semaphore.TryGetValue(L"test", &num2)));
REQUIRE(num1 == index);
REQUIRE(num2 == index);
}
};
// Test 32-bit values (edge cases)
TestValue(0u, 10u);
TestValue(250u, 260u);
TestValue(0x7FFFFFF0u, 0x7FFFFFFFu);
// Test 64-bit values (edge cases)
TestValue(0ull, 10ull);
TestValue(250ull, 260ull);
TestValue(0x000000007FFFFFF0ull, 0x000000008000000Full);
TestValue(0x00000000FFFFFFF0ull, 0x000000010000000Full);
TestValue(0x00000000FFFFFFF0ull, 0x000000010000000Full);
TestValue(0x3FFFFFFFFFFFFFF0ull, 0x3FFFFFFFFFFFFFFFull);
// Test pointer values
wil::details_abi::SemaphoreValue semaphore;
void* address = &semaphore;
REQUIRE(SUCCEEDED(semaphore.CreateFromPointer(L"test", address)));
void* ptr;
REQUIRE(SUCCEEDED(semaphore.TryGetPointer(L"test", &ptr)));
REQUIRE(ptr == address);
}
TEST_CASE("ResultTests::ProcessLocalStorage", "[result]")
{
// Test process local storage memory and ref-counting
{
wil::details_abi::ProcessLocalStorage<SharedObject> obj1("ver1");
wil::details_abi::ProcessLocalStorage<SharedObject> obj2("ver1");
auto& o1 = *obj1.GetShared();
auto& o2 = *obj2.GetShared();
REQUIRE(o1.value == 0);
REQUIRE(o2.value == 0);
o1.value = 42;
REQUIRE(o2.value == 42);
REQUIRE(objectCount == 1);
wil::details_abi::ProcessLocalStorage<SharedObject> obj3("ver3");
auto& o3 = *obj3.GetShared();
REQUIRE(o3.value == 0);
REQUIRE(objectCount == 2);
}
REQUIRE(objectCount == 0);
}
#ifdef WIL_ENABLE_EXCEPTIONS
#pragma warning(push)
#pragma warning(disable: 4702) // Unreachable code
TEST_CASE("ResultTests::ExceptionHandling", "[result]")
{
witest::TestFailureCache failures;
SECTION("Test 'what()' implementation on ResultException")
{
auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false);
try
{
THROW_HR(E_INVALIDARG);
FAIL("Expected an exception");
}
catch (const std::exception& exception)
{
REQUIRE(failures.size() == 1);
REQUIRE(failures[0].hr == E_INVALIDARG);
auto what = exception.what();
REQUIRE((what && *what));
REQUIRE(strstr(what, "Exception") != nullptr);
}
}
failures.clear();
SECTION("Test messaging from an unhandled std exception")
{
// #pragma warning(suppress: 28931) // unused assignment -- it IS being used... seems like a tool issue.
auto hr = []()
{
try
{
throw std::runtime_error("runtime");
}
catch (...)
{
RETURN_CAUGHT_EXCEPTION();
}
}();
REQUIRE(failures.size() == 1);
REQUIRE(failures[0].hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
REQUIRE(wcsstr(failures[0].pszMessage, L"runtime") != nullptr); // should get the exception what() string...
REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
}
failures.clear();
SECTION("Test messaging from bad_alloc")
{
auto hr = []() -> HRESULT
{
try
{
throw std::bad_alloc();
}
catch (...)
{
RETURN_CAUGHT_EXCEPTION();
}
}();
REQUIRE(failures.size() == 1);
REQUIRE(failures[0].hr == E_OUTOFMEMORY);
REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
REQUIRE(hr == E_OUTOFMEMORY);
}
failures.clear();
SECTION("Test messaging from a WIL exception")
{
auto hr = []() -> HRESULT
{
try
{
THROW_HR(E_INVALIDARG);
}
catch (...)
{
RETURN_CAUGHT_EXCEPTION();
}
return S_OK;
}();
REQUIRE(failures.size() == 2);
REQUIRE(failures[0].hr == E_INVALIDARG);
REQUIRE(failures[0].pszMessage == nullptr);
REQUIRE(failures[1].hr == E_INVALIDARG);
REQUIRE(wcsstr(failures[1].pszMessage, L"Exception") != nullptr); // should get the exception debug string...
REQUIRE(hr == E_INVALIDARG);
}
failures.clear();
SECTION("Fail fast an unknown exception")
{
REQUIRE(witest::DoesCodeCrash([]()
{
try
{
throw E_INVALIDARG; // bad throw... (long)
}
catch (...)
{
RETURN_CAUGHT_EXCEPTION();
}
}));
}
failures.clear();
SECTION("Log test (returns hr)")
{
HRESULT hr = S_OK;
try
{
throw std::bad_alloc();
}
catch (...)
{
hr = LOG_CAUGHT_EXCEPTION();
auto hrDirect = wil::ResultFromCaughtException();
REQUIRE(hr == hrDirect);
}
REQUIRE(failures.size() == 1);
REQUIRE(failures[0].hr == E_OUTOFMEMORY);
REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
REQUIRE(hr == E_OUTOFMEMORY);
}
failures.clear();
SECTION("Fail-fast test")
{
REQUIRE_CRASH([]()
{
try
{
throw std::bad_alloc();
}
catch (...)
{
FAIL_FAST_CAUGHT_EXCEPTION();
}
}());
}
failures.clear();
SECTION("Exception test (different exception type thrown...)")
{
auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false);
size_t line = 0;
try
{
try
{
throw std::bad_alloc();
}
catch (...)
{
line = __LINE__; THROW_NORMALIZED_CAUGHT_EXCEPTION();
}
}
catch (const wil::ResultException& exception)
{
REQUIRE(exception.GetFailureInfo().uLineNumber == line); // should have thrown new, so we should have the rethrow line number
REQUIRE(exception.GetErrorCode() == E_OUTOFMEMORY);
}
catch (...)
{
FAIL();
}
REQUIRE(failures.size() == 1);
REQUIRE(failures[0].hr == E_OUTOFMEMORY);
REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
}
failures.clear();
SECTION("Exception test (rethrow same exception type...)")
{
auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false);
size_t line = 0;
try
{
try
{
line = __LINE__; THROW_HR(E_OUTOFMEMORY);
}
catch (...)
{
THROW_NORMALIZED_CAUGHT_EXCEPTION();
}
}
catch (const wil::ResultException& exception)
{
REQUIRE(exception.GetFailureInfo().uLineNumber == line); // should have re-thrown the original exception (with the original line number)
}
catch (...)
{
FAIL();
}
}
failures.clear();
SECTION("Test catch message")
{
try
{
throw std::bad_alloc();
}
catch (...)
{
LOG_CAUGHT_EXCEPTION_MSG("train: %d", 42);
}
REQUIRE(failures.size() == 1);
REQUIRE(failures[0].hr == E_OUTOFMEMORY);
REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
REQUIRE(wcsstr(failures[0].pszMessage, L"train") != nullptr); // should *also* get the message...
REQUIRE(wcsstr(failures[0].pszMessage, L"42") != nullptr);
}
failures.clear();
SECTION("Test messaging from a WIL exception")
{
auto hr = []() -> HRESULT
{
try
{
throw std::bad_alloc();
}
catch (...)
{
RETURN_CAUGHT_EXCEPTION_EXPECTED();
}
}();
REQUIRE(failures.size() == 0);
REQUIRE(hr == E_OUTOFMEMORY);
}
failures.clear();
SECTION("Test ResultFromException...")
{
auto hrOk = wil::ResultFromException([&]
{
});
REQUIRE(hrOk == S_OK);
auto hr = wil::ResultFromException([&]
{
throw std::bad_alloc();
});
REQUIRE(failures.size() == 0);
REQUIRE(hr == E_OUTOFMEMORY);
}
failures.clear();
SECTION("Explicit failfast for unrecognized")
{
REQUIRE_CRASH(wil::ResultFromException([&]
{
throw E_FAIL;
}));
}
failures.clear();
SECTION("Manual debug-only validation of the SEH failfast")
{
auto hr1 = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]()
{
// Uncomment to test SEH fail-fast
// throw E_FAIL;
});
REQUIRE(hr1 == S_OK);
auto hr2 = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::Thrown, [&]
{
// Uncomment to test SEH fail-fast
// throw std::range_error("range");
});
REQUIRE(hr2 == S_OK);
wil::FailFastException(WI_DIAGNOSTICS_INFO, [&]
{
// Uncomment to test SEH fail-fast
// THROW_HR(E_FAIL);
});
}
failures.clear();
SECTION("Standard")
{
auto line = __LINE__; auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]
{
THROW_HR(E_INVALIDARG);
});
REQUIRE(failures.size() == 2);
REQUIRE(static_cast<decltype(line)>(failures[1].uLineNumber) == line);
REQUIRE(hr == E_INVALIDARG);
}
failures.clear();
SECTION("bad_alloc")
{
auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]
{
throw std::bad_alloc();
});
REQUIRE(failures.size() == 1);
REQUIRE(hr == E_OUTOFMEMORY);
}
failures.clear();
SECTION("std::exception")
{
auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]
{
throw std::range_error("range");
});
REQUIRE(failures.size() == 1);
REQUIRE(wcsstr(failures[0].pszMessage, L"range") != nullptr);
REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
}
}
void ExceptionHandlingCompilationTest()
{
[]{ try { throw std::bad_alloc(); } CATCH_RETURN(); }();
[]{ try { throw std::bad_alloc(); } CATCH_RETURN_MSG("train: %d", 42); }();
[]{ try { throw std::bad_alloc(); } CATCH_RETURN_EXPECTED(); }();
[]{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION(); } }();
[]{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION_MSG("train: %d", 42); } }();
[]{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION_EXPECTED(); } }();
try { throw std::bad_alloc(); } CATCH_LOG();
try { throw std::bad_alloc(); } CATCH_LOG_MSG("train: %d", 42);
try { throw std::bad_alloc(); } catch (...) { LOG_CAUGHT_EXCEPTION(); }
try { throw std::bad_alloc(); } catch (...) { LOG_CAUGHT_EXCEPTION_MSG("train: %d", 42); }
try { throw std::bad_alloc(); } CATCH_FAIL_FAST();
try { throw std::bad_alloc(); } CATCH_FAIL_FAST_MSG("train: %d", 42);
try { throw std::bad_alloc(); } catch (...) { FAIL_FAST_CAUGHT_EXCEPTION(); }
try { throw std::bad_alloc(); } catch (...) { FAIL_FAST_CAUGHT_EXCEPTION_MSG("train: %d", 42); }
try { try { throw std::bad_alloc(); } CATCH_THROW_NORMALIZED(); } catch (...) {}
try { try { throw std::bad_alloc(); } CATCH_THROW_NORMALIZED_MSG("train: %d", 42); } catch (...) {}
try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION(); } } catch (...) {}
try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION_MSG("train: %d", 42); } } catch (...) {}
HRESULT hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::All, [&]
{
THROW_HR(E_FAIL);
});
hr = wil::ResultFromException(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::None, [&]
{
});
hr = wil::ResultFromException([&]
{
});
wil::FailFastException(WI_DIAGNOSTICS_INFO, [&]
{
});
}
#pragma warning(pop)
#endif
TEST_CASE("ResultTests::ErrorMacros", "[result]")
{
REQUIRE_ERROR(FAIL_FAST());
REQUIRE_ERROR(FAIL_FAST_IF(true));
REQUIRE_ERROR(FAIL_FAST_IF_NULL(nullptr));
REQUIRE_NOERROR(FAIL_FAST_IF(false));
REQUIRE_NOERROR(FAIL_FAST_IF_NULL(_ReturnAddress()));
REQUIRE_ERROR(FAIL_FAST_MSG("%d", 42));
REQUIRE_ERROR(FAIL_FAST_IF_MSG(true, "%d", 42));
REQUIRE_ERROR(FAIL_FAST_IF_NULL_MSG(nullptr, "%d", 42));
REQUIRE_NOERROR(FAIL_FAST_IF_MSG(false, "%d", 42));
REQUIRE_NOERROR(FAIL_FAST_IF_NULL_MSG(_ReturnAddress(), "%d", 42));
//wil::g_pfnResultLoggingCallback = ResultMacrosLoggingCallback;
SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
REQUIRE_ERROR(__FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(FALSE));
REQUIRE_NOERROR(__FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(TRUE));
}
// The originate helper isn't compatible with CX so don't test it in that mode.
#ifndef __cplusplus_winrt
TEST_CASE("ResultTests::NoOriginationByDefault", "[result]")
{
::wil::SetOriginateErrorCallback(nullptr);
wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
// We can't guarantee test order, so clear the error payload prior to starting
SetRestrictedErrorInfo(nullptr);
[]() -> HRESULT
{
RETURN_HR(S_OK);
}();
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
#ifdef WIL_ENABLE_EXCEPTIONS
try
{
THROW_HR(E_FAIL);
}
catch (...) {}
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
#endif // WIL_ENABLE_EXCEPTIONS
[]() -> HRESULT
{
RETURN_HR(E_FAIL);
}();
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
[]() -> HRESULT
{
RETURN_IF_FAILED_EXPECTED(E_ACCESSDENIED);
return S_OK;
}();
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
}
TEST_CASE("ResultTests::AutomaticOriginationOnFailure", "[result]")
{
::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions);
wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
// Make sure we don't start with an error payload
SetRestrictedErrorInfo(nullptr);
// Success codes shouldn't originate.
[]()
{
RETURN_HR(S_OK);
}();
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
auto validateOriginatedError = [&](HRESULT hrExpected)
{
wil::unique_bstr descriptionUnused;
HRESULT existingHr = S_OK;
wil::unique_bstr restrictedDescriptionUnused;
wil::unique_bstr capabilitySidUnused;
REQUIRE_SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused));
REQUIRE(hrExpected == existingHr);
};
#ifdef WIL_ENABLE_EXCEPTIONS
// Throwing an error should originate.
constexpr HRESULT thrownErrorCode = TYPE_E_ELEMENTNOTFOUND;
try
{
THROW_HR(thrownErrorCode);
}
catch (...) {}
REQUIRE(S_OK == GetRestrictedErrorInfo(&restrictedErrorInformation));
validateOriginatedError(thrownErrorCode);
#endif // WIL_ENABLE_EXCEPTIONS
// Returning an error code should originate.
static constexpr HRESULT returnedErrorCode = REGDB_E_CLASSNOTREG;
[]()
{
RETURN_HR(returnedErrorCode);
}();
REQUIRE(S_OK == GetRestrictedErrorInfo(&restrictedErrorInformation));
validateOriginatedError(returnedErrorCode);
// _EXPECTED errors should NOT originate.
static constexpr HRESULT expectedErrorCode = E_ACCESSDENIED;
[]()
{
RETURN_IF_FAILED_EXPECTED(expectedErrorCode);
return S_OK;
}();
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
}
#endif // __cplusplus_winrt

137
Externals/WIL/tests/Rpc.cpp vendored Normal file
View file

@ -0,0 +1,137 @@
#include "common.h"
#include <wil/rpc_helpers.h>
void RpcMethodReturnsVoid(ULONG toRaise)
{
if (toRaise)
{
RaiseException(toRaise, 0, 0, nullptr);
}
}
struct FOO_CONTEXT_T {};
typedef FOO_CONTEXT_T* FOO_CONTEXT;
typedef FOO_CONTEXT* PFOO_CONTEXT;
void CloseContextHandle(_Inout_ PFOO_CONTEXT)
{
}
void CloseContextHandleRaise(_Inout_ PFOO_CONTEXT)
{
return RpcMethodReturnsVoid(RPC_X_BAD_STUB_DATA);
}
HRESULT AcquireContextHandle(_In_ handle_t binding, _Out_ PFOO_CONTEXT context)
{
*context = reinterpret_cast<FOO_CONTEXT>(binding);
return S_OK;
}
HRESULT RpcMethodReturnsHResult(HRESULT toReturn, ULONG toRaise)
{
RpcMethodReturnsVoid(toRaise);
return toReturn;
}
GUID RpcMethodReturnsGuid(ULONG toRaise)
{
RpcMethodReturnsVoid(toRaise);
return __uuidof(IUnknown);
}
TEST_CASE("Rpc::NonThrowing", "[rpc]")
{
SECTION("Success paths")
{
REQUIRE_SUCCEEDED(wil::invoke_rpc_nothrow(RpcMethodReturnsVoid, 0UL));
REQUIRE_SUCCEEDED(wil::invoke_rpc_nothrow(RpcMethodReturnsHResult, S_OK, 0UL));
GUID tmp{};
REQUIRE_SUCCEEDED(wil::invoke_rpc_result_nothrow(tmp, RpcMethodReturnsGuid, 0UL));
REQUIRE(tmp == __uuidof(IUnknown));
}
SECTION("Failures in the method")
{
REQUIRE(wil::invoke_rpc_nothrow(RpcMethodReturnsHResult, E_CHANGED_STATE, 0) == E_CHANGED_STATE);
}
SECTION("Failures in the fabric")
{
REQUIRE(wil::invoke_rpc_nothrow(RpcMethodReturnsVoid, RPC_S_CALL_FAILED) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED));
REQUIRE(wil::invoke_rpc_nothrow(RpcMethodReturnsHResult, E_CHANGED_STATE, RPC_S_CALL_FAILED) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED));
GUID tmp{};
REQUIRE(wil::invoke_rpc_result_nothrow(tmp, RpcMethodReturnsGuid, RPC_S_CALL_FAILED) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED));
}
SECTION("Context Handles")
{
using foo_context_t = wil::unique_rpc_context_handle<FOO_CONTEXT, decltype(&CloseContextHandle), CloseContextHandle>;
foo_context_t ctx;
auto tempBinding = reinterpret_cast<handle_t>(-5);
REQUIRE_SUCCEEDED(wil::invoke_rpc_nothrow(AcquireContextHandle, tempBinding, ctx.put()));
REQUIRE(ctx.get() == reinterpret_cast<FOO_CONTEXT>(tempBinding));
ctx.reset();
}
SECTION("Context Handles Close Raised")
{
using foo_context_t = wil::unique_rpc_context_handle<FOO_CONTEXT, decltype(&CloseContextHandleRaise), CloseContextHandleRaise>;
foo_context_t ctx{ reinterpret_cast<FOO_CONTEXT>(42) };
ctx.reset();
}
}
#ifdef WIL_ENABLE_EXCEPTIONS
#include <sstream>
class WilExceptionMatcher : public Catch::MatcherBase<wil::ResultException>
{
HRESULT m_expected;
public:
WilExceptionMatcher(HRESULT ex) : m_expected(ex) { }
bool match(wil::ResultException const& ex) const override {
return ex.GetErrorCode() == m_expected;
}
std::string describe() const override {
std::ostringstream ss;
ss << "wil::ResultException expects code 0x%08lx" << std::hex << m_expected;
return ss.str();
}
};
#define REQUIRE_THROWS_WIL_HR(hr, expr) REQUIRE_THROWS_MATCHES(expr, wil::ResultException, WilExceptionMatcher(hr))
TEST_CASE("Rpc::Throwing", "[rpc]")
{
SECTION("Success paths")
{
REQUIRE_NOTHROW(wil::invoke_rpc(RpcMethodReturnsVoid, 0UL));
GUID tmp{};
REQUIRE_NOTHROW(tmp = wil::invoke_rpc_result(RpcMethodReturnsGuid, 0UL));
REQUIRE(tmp == __uuidof(IUnknown));
}
SECTION("Failures in the method")
{
REQUIRE_THROWS_WIL_HR(E_CHANGED_STATE, wil::invoke_rpc(RpcMethodReturnsHResult, E_CHANGED_STATE, 0UL));
}
SECTION("Failures in the fabric")
{
REQUIRE_THROWS_WIL_HR(HRESULT_FROM_WIN32(RPC_S_CALL_FAILED), wil::invoke_rpc(RpcMethodReturnsVoid, RPC_S_CALL_FAILED));
REQUIRE_THROWS_WIL_HR(HRESULT_FROM_WIN32(RPC_S_CALL_FAILED), wil::invoke_rpc(RpcMethodReturnsHResult, E_CHANGED_STATE, RPC_S_CALL_FAILED));
GUID tmp{};
REQUIRE_THROWS_WIL_HR(HRESULT_FROM_WIN32(RPC_S_CALL_FAILED), tmp = wil::invoke_rpc_result(RpcMethodReturnsGuid, RPC_S_CALL_FAILED));
REQUIRE(tmp == GUID{});
}
}
#endif

571
Externals/WIL/tests/SafeCastTests.cpp vendored Normal file
View file

@ -0,0 +1,571 @@
#include <wil/safecast.h>
#include "common.h"
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("SafeCastTests::SafeCastThrowsTemplateCheck", "[safecast]")
{
// In all cases, a value of '1' should be cast-able to any signed or unsigned integral type without error
SECTION("Unqualified char")
{
char orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
// wil::safe_cast<unsigned char> (orig); // No available conversion in intsafe
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
// wil::safe_cast<unsigned short> (orig); // No available conversion in intsafe
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
// wil::safe_cast<unsigned int> (orig); // No available conversion in intsafe
wil::safe_cast<long> (orig);
// wil::safe_cast<unsigned long> (orig); // No available conversion in intsafe
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
// wil::safe_cast<unsigned __int64> (orig); // No available conversion in intsafe
wil::safe_cast<__int3264> (orig);
// wil::safe_cast<unsigned __int3264>(orig); // No available conversion in intsafe
// wil::safe_cast<wchar_t> (orig); // No available conversion in intsafe
}
SECTION("Signed char")
{
signed char orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unsigned char")
{
unsigned char orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unqualified short")
{
short orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Signed short")
{
signed short orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unsigned short")
{
unsigned short orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unqualified int")
{
int orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Signed int")
{
signed int orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unsigned int")
{
unsigned int orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unqualified long")
{
long orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unsigned log")
{
unsigned long orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unqualified int64")
{
__int64 orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Signed int64")
{
signed __int64 orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unsigned int64")
{
unsigned __int64 orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unqualified int3264")
{
__int3264 orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("Unsigned int3264")
{
unsigned __int3264 orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
SECTION("wchar_t")
{
wchar_t orig = 1;
wil::safe_cast<char> (orig);
wil::safe_cast<signed char> (orig);
wil::safe_cast<unsigned char> (orig);
wil::safe_cast<short> (orig);
wil::safe_cast<signed short> (orig);
wil::safe_cast<unsigned short> (orig);
wil::safe_cast<int> (orig);
wil::safe_cast<signed int> (orig);
wil::safe_cast<unsigned int> (orig);
wil::safe_cast<long> (orig);
wil::safe_cast<unsigned long> (orig);
wil::safe_cast<__int64> (orig);
wil::safe_cast<signed __int64> (orig);
wil::safe_cast<unsigned __int64> (orig);
wil::safe_cast<__int3264> (orig);
wil::safe_cast<unsigned __int3264>(orig);
wil::safe_cast<wchar_t> (orig);
}
}
#endif
TEST_CASE("SafeCastTests::SafeCastFailFastSyntaxCheck", "[safecast]")
{
SECTION("safe_cast_failfast safe")
{
INT i = INT_MAX;
LONG l = wil::safe_cast_failfast<LONG>(i);
REQUIRE(l == INT_MAX);
}
SECTION("safe_cast_failfast unsafe")
{
INT i = 0;
SHORT s = wil::safe_cast_failfast<SHORT>(i);
REQUIRE(s == 0);
}
SECTION("safe_cast_failfast unsafe to wchar_t")
{
INT i = 0;
wchar_t wc = wil::safe_cast_failfast<wchar_t>(i);
REQUIRE(wc == 0);
}
SECTION("safe_cast_failfast unsafe from wchar_t")
{
wchar_t wc = 0;
unsigned char uc = wil::safe_cast_failfast<unsigned char>(wc);
REQUIRE(uc == 0);
}
}
TEST_CASE("SafeCastTests::SafeCastNoThrowSyntaxCheck", "[safecast]")
{
SECTION("safe_cast_nothrow safe")
{
INT i = INT_MAX;
LONG l = wil::safe_cast_nothrow<LONG>(i);
REQUIRE(l == INT_MAX);
}
// safe_cast_nothrow one parameter unsafe, throws compiler error as expected
// {
// __int64 i64 = 0;
// int i = wil::safe_cast_nothrow<int>(i64);
// }
SECTION("safe_cast_nothrow two parameter potentially unsafe due to usage of variable sized types")
{
SIZE_T st = 0;
UINT ui;
auto result = wil::safe_cast_nothrow<UINT>(st, &ui);
REQUIRE_SUCCEEDED(result);
REQUIRE(ui == 0);
}
// safe_cast_nothrow two parameter known safe, throws compiler error as expected
// {
// unsigned char uc = 0;
// unsigned short us;
// auto result = wil::safe_cast_nothrow(uc, &us);
// }
SECTION("safe_cast_nothrow unsafe")
{
INT i = 0;
SHORT s;
auto result = wil::safe_cast_nothrow<SHORT>(i, &s);
REQUIRE_SUCCEEDED(result);
REQUIRE(s == 0);
}
SECTION("safe_cast_nothrow unsafe to wchar_t")
{
INT i = 0;
wchar_t wc;
auto result = wil::safe_cast_nothrow<wchar_t>(i, &wc);
REQUIRE_SUCCEEDED(result);
REQUIRE(wc == 0);
}
SECTION("safe_cast_nothrow unsafe from wchar_t")
{
wchar_t wc = 0;
unsigned char uc;
auto result = wil::safe_cast_nothrow<unsigned char>(wc, &uc);
REQUIRE_SUCCEEDED(result);
REQUIRE(uc == 0);
}
}
TEST_CASE("SafeCastTests::SafeCastNoFailures", "[safecast]")
{
SECTION("INT -> LONG")
{
INT i = INT_MAX;
LONG l = wil::safe_cast_nothrow<LONG>(i);
REQUIRE(l == INT_MAX);
}
SECTION("LONG -> INT")
{
LONG l = LONG_MAX;
INT i = wil::safe_cast_nothrow<INT>(l);
REQUIRE(i == LONG_MAX);
}
SECTION("INT -> UINT")
{
INT i = INT_MAX;
UINT ui = wil::safe_cast_failfast<UINT>(i);
REQUIRE(ui == INT_MAX);
}
SECTION("SIZE_T -> SIZE_T")
{
SIZE_T st = SIZE_T_MAX;
SIZE_T st2 = wil::safe_cast_failfast<SIZE_T>(st);
REQUIRE(st2 == SIZE_T_MAX);
}
SECTION("wchar_t -> uint")
{
wchar_t wc = 0;
UINT ui = wil::safe_cast_failfast<UINT>(wc);
REQUIRE(ui == 0);
}
SECTION("wchar_t -> unsigned char")
{
wchar_t wc = 0;
unsigned char uc = wil::safe_cast_failfast<unsigned char>(wc);
REQUIRE(uc == 0);
auto result = wil::safe_cast_nothrow<unsigned char>(wc, &uc);
REQUIRE_SUCCEEDED(result);
}
SECTION("uint -> wchar_t")
{
UINT ui = 0;
wchar_t wc = wil::safe_cast_failfast<wchar_t>(ui);
REQUIRE(wc == 0);
auto result = wil::safe_cast_nothrow<wchar_t>(ui, &wc);
REQUIRE_SUCCEEDED(result);
}
#ifndef _WIN64
SECTION("SIZE_T -> UINT")
{
SIZE_T st = SIZE_T_MAX;
UINT ui = wil::safe_cast_nothrow<UINT>(st);
REQUIRE(ui == SIZE_T_MAX);
}
#endif
}
TEST_CASE("SafeCastTests::SafeCastNoThrowFail", "[safecast]")
{
SECTION("size_t -> short")
{
size_t st = SIZE_T_MAX;
short s;
REQUIRE_FAILED(wil::safe_cast_nothrow(st, &s));
}
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("SafeCastTests::SafeCastExpectFailFast", "[safecast]")
{
// Template for safe_cast fail fast tests, fill out more instances needed
witest::TestFailureCache failures;
failures.clear();
{
size_t st = SIZE_T_MAX;
REQUIRE_CRASH(wil::safe_cast_failfast<short>(st));
REQUIRE(failures.size() == 1);
}
failures.clear();
{
size_t st = SIZE_T_MAX;
REQUIRE_THROWS(wil::safe_cast<short>(st));
REQUIRE(failures.size() == 1);
}
}
#endif

47
Externals/WIL/tests/StlTests.cpp vendored Normal file
View file

@ -0,0 +1,47 @@
#include <wil/stl.h>
#include "common.h"
#ifndef WIL_ENABLE_EXCEPTIONS
#error STL tests require exceptions
#endif
struct dummy
{
char value;
};
// Specialize std::allocator<> so that we don't actually allocate/deallocate memory
dummy g_memoryBuffer[256];
namespace std
{
template <>
struct allocator<dummy>
{
using value_type = dummy;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
dummy* allocate(std::size_t count)
{
REQUIRE(count <= std::size(g_memoryBuffer));
return g_memoryBuffer;
}
void deallocate(dummy* ptr, std::size_t count)
{
for (std::size_t i = 0; i < count; ++i)
{
REQUIRE(ptr[i].value == 0);
}
}
};
}
TEST_CASE("StlTests::TestSecureAllocator", "[stl][secure_allocator]")
{
{
wil::secure_vector<dummy> sensitiveBytes(32, dummy{ 'a' });
}
}

View file

@ -0,0 +1,320 @@
#include <wil/token_helpers.h>
#include "common.h"
TEST_CASE("TokenHelpersTests::VerifyOpenCurrentAccessTokenNoThrow", "[token_helpers]")
{
// Open current thread/process access token
wil::unique_handle token;
REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token));
REQUIRE(token != nullptr);
REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ));
REQUIRE(token != nullptr);
REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ, wil::OpenThreadTokenAs::Current));
REQUIRE(token != nullptr);
REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ, wil::OpenThreadTokenAs::Self));
REQUIRE(token != nullptr);
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("TokenHelpersTests::VerifyOpenCurrentAccessToken", "[token_helpers]")
{
// Open current thread/process access token
wil::unique_handle token(wil::open_current_access_token());
REQUIRE(token != nullptr);
token = wil::open_current_access_token(TOKEN_READ);
REQUIRE(token != nullptr);
token = wil::open_current_access_token(TOKEN_READ, wil::OpenThreadTokenAs::Current);
REQUIRE(token != nullptr);
token = wil::open_current_access_token(TOKEN_READ, wil::OpenThreadTokenAs::Self);
REQUIRE(token != nullptr);
}
#endif
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationNoThrow", "[token_helpers]")
{
SECTION("Passing a null token")
{
wistd::unique_ptr<TOKEN_USER> tokenInfo;
REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, nullptr));
REQUIRE(tokenInfo != nullptr);
}
SECTION("Passing a non null token, since it a fake token there is no tokenInfo and hence should fail, code path is correct")
{
HANDLE faketoken = GetStdHandle(STD_INPUT_HANDLE);
wistd::unique_ptr<TOKEN_USER> tokenInfo;
REQUIRE_FAILED(wil::get_token_information_nothrow(tokenInfo, faketoken));
}
}
// Pseudo tokens can be passed to token APIs and avoid the handle allocations
// making use more efficient.
TEST_CASE("TokenHelpersTests::DemonstrateUseWithPseudoTokens", "[token_helpers]")
{
wistd::unique_ptr<TOKEN_USER> tokenInfo;
REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, GetCurrentProcessToken()));
REQUIRE(tokenInfo != nullptr);
REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, GetCurrentThreadEffectiveToken()));
REQUIRE(tokenInfo != nullptr);
// No thread token by default, this should fail
REQUIRE_FAILED(wil::get_token_information_nothrow(tokenInfo, GetCurrentThreadToken()));
REQUIRE(tokenInfo == nullptr);
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformation", "[token_helpers]")
{
// Passing a null token
wistd::unique_ptr<TOKEN_USER> tokenInfo(wil::get_token_information<TOKEN_USER>(nullptr));
REQUIRE(tokenInfo != nullptr);
}
#endif
// This fails with 'ERROR_NO_SUCH_LOGON_SESSION' on the CI machines, so disable
#ifndef WIL_FAST_BUILD
TEST_CASE("TokenHelpersTests::VerifyLinkedToken", "[token_helpers]")
{
wil::unique_token_linked_token theToken;
REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(theToken, nullptr));
#ifdef WIL_ENABLE_EXCEPTIONS
REQUIRE_NOTHROW(wil::get_linked_token_information());
#endif
}
#endif
bool IsImpersonating()
{
wil::unique_handle token;
if (!::OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token))
{
WI_ASSERT(::GetLastError() == ERROR_NO_TOKEN);
return false;
}
return true;
}
wil::unique_handle GetTokenToImpersonate()
{
wil::unique_handle processToken;
FAIL_FAST_IF_WIN32_BOOL_FALSE(::OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &processToken));
wil::unique_handle impersonateToken;
FAIL_FAST_IF_WIN32_BOOL_FALSE(::DuplicateToken(processToken.get(), SecurityImpersonation, &impersonateToken));
return impersonateToken;
}
TEST_CASE("TokenHelpersTests::VerifyResetThreadTokenNoThrow", "[token_helpers]")
{
auto impersonateToken = GetTokenToImpersonate();
// Set the thread into a known state - no token.
wil::unique_token_reverter clearThreadToken;
REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadToken));
REQUIRE_FALSE(IsImpersonating());
// Set a token on the thread - the process token, guaranteed to be friendly
wil::unique_token_reverter setThreadToken1;
REQUIRE_SUCCEEDED(wil::impersonate_token_nothrow(impersonateToken.get(), setThreadToken1));
REQUIRE(IsImpersonating());
SECTION("Clear the token again, should be not impersonating, explicit reset")
{
wil::unique_token_reverter clearThreadAgain;
REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadAgain));
REQUIRE_FALSE(IsImpersonating());
clearThreadAgain.reset();
REQUIRE(IsImpersonating());
}
SECTION("Clear the token again, should be not impersonating, dtor reset")
{
wil::unique_token_reverter clearThreadAgain;
REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadAgain));
REQUIRE_FALSE(IsImpersonating());
}
REQUIRE(IsImpersonating());
// Clear what we were impersonating
setThreadToken1.reset();
REQUIRE_FALSE(IsImpersonating());
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("TokenHelpersTests::VerifyResetThreadToken", "[token_helpers]")
{
auto impersonateToken = GetTokenToImpersonate();
// Set the thread into a known state - no token.
auto clearThreadToken = wil::run_as_self();
REQUIRE_FALSE(IsImpersonating());
// Set a token on the thread - the process token, guaranteed to be friendly
auto setThreadToken1 = wil::impersonate_token(impersonateToken.get());
REQUIRE(IsImpersonating());
SECTION("Clear the token again, should be not impersonating, explicit reset")
{
auto clearThreadAgain = wil::run_as_self();
REQUIRE_FALSE(IsImpersonating());
clearThreadAgain.reset();
REQUIRE(IsImpersonating());
}
SECTION("Clear the token again, should be not impersonating, dtor reset")
{
auto clearThreadAgain = wil::run_as_self();
REQUIRE_FALSE(IsImpersonating());
}
REQUIRE(IsImpersonating());
// Clear what we were impersonating
setThreadToken1.reset();
REQUIRE_FALSE(IsImpersonating());
}
#endif // WIL_ENABLE_EXCEPTIONS
template <typename T, wistd::enable_if_t<!wil::details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
void TestGetTokenInfoForCurrentThread()
{
wistd::unique_ptr<T> tokenInfo;
const auto hr = wil::get_token_information_nothrow(tokenInfo, nullptr);
REQUIRE(S_OK == hr);
}
template <typename T, wistd::enable_if_t<wil::details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
void TestGetTokenInfoForCurrentThread()
{
T tokenInfo{};
const auto hr = wil::get_token_information_nothrow(&tokenInfo, nullptr);
REQUIRE(S_OK == hr);
}
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformation2", "[token_helpers]")
{
// Variable sized cases
TestGetTokenInfoForCurrentThread<TOKEN_ACCESS_INFORMATION>();
TestGetTokenInfoForCurrentThread<TOKEN_APPCONTAINER_INFORMATION>();
TestGetTokenInfoForCurrentThread<TOKEN_DEFAULT_DACL>();
TestGetTokenInfoForCurrentThread<TOKEN_GROUPS_AND_PRIVILEGES>();
TestGetTokenInfoForCurrentThread<TOKEN_MANDATORY_LABEL>();
TestGetTokenInfoForCurrentThread<TOKEN_OWNER>();
TestGetTokenInfoForCurrentThread<TOKEN_PRIMARY_GROUP>();
TestGetTokenInfoForCurrentThread<TOKEN_PRIVILEGES>();
TestGetTokenInfoForCurrentThread<TOKEN_USER>();
// Fixed size and reports size using ERROR_INSUFFICIENT_BUFFER (perf opportunity, ignore second allocation)
TestGetTokenInfoForCurrentThread<TOKEN_ELEVATION_TYPE>();
TestGetTokenInfoForCurrentThread<TOKEN_MANDATORY_POLICY>();
TestGetTokenInfoForCurrentThread<TOKEN_ORIGIN>();
TestGetTokenInfoForCurrentThread<TOKEN_SOURCE>();
TestGetTokenInfoForCurrentThread<TOKEN_STATISTICS>();
TestGetTokenInfoForCurrentThread<TOKEN_TYPE>();
}
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationBadLength", "[token_helpers]")
{
// Fixed size and reports size using ERROR_BAD_LENGTH (bug)
TestGetTokenInfoForCurrentThread<TOKEN_ELEVATION>();
}
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationSecurityImpersonationLevelErrorCases", "[token_helpers]")
{
SECURITY_IMPERSONATION_LEVEL tokenInfo{};
// SECURITY_IMPERSONATION_LEVEL does not support the effective token when it is implicit.
// Demonstrate the error return in that case.
REQUIRE(E_INVALIDARG == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadEffectiveToken()));
// Using an explicit token is supported but returns ERROR_NO_TOKEN when there is no
// impersonation token be sure to use RETURN_IF_FAILED_EXPECTED() and don't use
// the exception forms if this case is not expected.
REQUIRE(HRESULT_FROM_WIN32(ERROR_NO_TOKEN) == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadToken()));
// Setup the impersonation token that SECURITY_IMPERSONATION_LEVEL requires.
FAIL_FAST_IF_WIN32_BOOL_FALSE(ImpersonateSelf(SecurityIdentification));
TestGetTokenInfoForCurrentThread<SECURITY_IMPERSONATION_LEVEL>();
REQUIRE(S_OK == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadToken()));
RevertToSelf();
}
bool operator==(const SID_IDENTIFIER_AUTHORITY& left, const SID_IDENTIFIER_AUTHORITY& right)
{
return memcmp(&left, &right, sizeof(left)) == 0;
}
TEST_CASE("TokenHelpersTests::StaticSid", "[token_helpers]")
{
SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY;
auto staticSid = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS);
auto largerSid = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS, DOMAIN_ALIAS_RID_BACKUP_OPS);
largerSid = staticSid;
largerSid = largerSid;
// staticSid = largerSid; // Uncommenting this correctly fails to compile.
REQUIRE(IsValidSid(staticSid.get()));
REQUIRE(*GetSidSubAuthorityCount(staticSid.get()) == 2);
REQUIRE(*GetSidIdentifierAuthority(staticSid.get()) == ntAuthority);
REQUIRE(*GetSidSubAuthority(staticSid.get(), 0) == SECURITY_BUILTIN_DOMAIN_RID);
REQUIRE(*GetSidSubAuthority(staticSid.get(), 1) == DOMAIN_ALIAS_RID_GUESTS);
}
TEST_CASE("TokenHelpersTests::TestMembership", "[token_helpers]")
{
bool member;
REQUIRE_SUCCEEDED(wil::test_token_membership_nothrow(
&member,
GetCurrentThreadEffectiveToken(),
SECURITY_NT_AUTHORITY,
SECURITY_AUTHENTICATED_USER_RID));
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("TokenHelpersTests::VerifyGetTokenInfo", "[token_helpers]")
{
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_APPCONTAINER_INFORMATION>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_ELEVATION_TYPE>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_MANDATORY_POLICY>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_ORIGIN>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_STATISTICS>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_TYPE>());
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_ELEVATION>());
// check a non-pointer size value to make sure the whole struct is returned.
DWORD resultSize{};
TOKEN_SOURCE ts{};
auto tokenSource = wil::get_token_information<TOKEN_SOURCE>();
GetTokenInformation(GetCurrentThreadEffectiveToken(), TokenSource, &ts, sizeof(ts), &resultSize);
REQUIRE(memcmp(&ts, &tokenSource, sizeof(ts)) == 0);
}
TEST_CASE("TokenHelpersTests::VerifyGetTokenInfoFailFast", "[token_helpers]")
{
// fixed size
REQUIRE_NOTHROW(wil::get_token_information_failfast<TOKEN_APPCONTAINER_INFORMATION>());
// variable size
REQUIRE_NOTHROW(wil::get_token_information_failfast<TOKEN_OWNER>());
}
TEST_CASE("TokenHelpersTests::Verify_impersonate_token", "[token_helpers]")
{
auto impersonationToken = wil::impersonate_token();
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_TYPE>());
}
#endif // WIL_ENABLE_EXCEPTIONS

View file

@ -0,0 +1,266 @@
#include <wil/winrt.h>
#include <wrl/implements.h>
#include "common.h"
using namespace ABI::Windows::Foundation;
using namespace Microsoft::WRL;
namespace wiltest
{
class AbiTestEventSender WrlFinal : public RuntimeClass<
RuntimeClassFlags<RuntimeClassType::WinRtClassicComMix>,
IClosable,
IMemoryBufferReference,
FtmBase>
{
public:
// IMemoryBufferReference
IFACEMETHODIMP get_Capacity(_Out_ UINT32* value)
{
*value = 0;
return S_OK;
}
IFACEMETHODIMP add_Closed(
_In_ ITypedEventHandler<IMemoryBufferReference*, IInspectable*>* handler,
_Out_ ::EventRegistrationToken* token)
{
return m_closedEvent.Add(handler, token);
}
IFACEMETHODIMP remove_Closed(::EventRegistrationToken token)
{
return m_closedEvent.Remove(token);
}
// IClosable
IFACEMETHODIMP Close()
{
RETURN_IF_FAILED(m_closedEvent.InvokeAll(this, nullptr));
return S_OK;
}
private:
Microsoft::WRL::EventSource<ITypedEventHandler<IMemoryBufferReference*, IInspectable*>> m_closedEvent;
};
}
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenEventSubscribe", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);
int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);
{
wil::unique_winrt_event_token<IMemoryBufferReference> token;
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token));
REQUIRE(static_cast<bool>(token));
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);
}
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);
}
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenEarlyReset", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);
int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);
wil::unique_winrt_event_token<IMemoryBufferReference> token;
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token));
REQUIRE(static_cast<bool>(token));
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);
token.reset();
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);
}
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenMoveTokenToDifferentScope", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);
int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);
wil::unique_winrt_event_token<IMemoryBufferReference> outerToken;
REQUIRE_FALSE(static_cast<bool>(outerToken));
{
wil::unique_winrt_event_token<IMemoryBufferReference> token;
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token));
REQUIRE(static_cast<bool>(token));
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);
outerToken = std::move(token);
REQUIRE_FALSE(static_cast<bool>(token));
REQUIRE(static_cast<bool>(outerToken));
}
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 2);
}
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenMoveConstructor", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);
int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);
wil::unique_winrt_event_token<IMemoryBufferReference> firstToken;
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &firstToken));
REQUIRE(static_cast<bool>(firstToken));
closable->Close();
REQUIRE(timesInvoked == 1);
wil::unique_winrt_event_token<IMemoryBufferReference> secondToken(std::move(firstToken));
REQUIRE_FALSE(static_cast<bool>(firstToken));
REQUIRE(static_cast<bool>(secondToken));
closable->Close();
REQUIRE(timesInvoked == 2);
firstToken.reset();
closable->Close();
REQUIRE(timesInvoked == 3);
secondToken.reset();
closable->Close();
REQUIRE(timesInvoked == 3);
}
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenReleaseAndReattachToNewWrapper", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);
int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);
wil::unique_winrt_event_token<IMemoryBufferReference> firstToken;
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &firstToken));
REQUIRE(static_cast<bool>(firstToken));
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 1);
::EventRegistrationToken rawToken = firstToken.release();
REQUIRE_FALSE(static_cast<bool>(firstToken));
REQUIRE(rawToken.value != 0);
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 2);
wil::unique_winrt_event_token<IMemoryBufferReference> secondToken(
rawToken, testEventSender.Get(), &IMemoryBufferReference::remove_Closed);
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 3);
secondToken.reset();
REQUIRE_SUCCEEDED(closable->Close());
REQUIRE(timesInvoked == 3);
}
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenPolicyVariants", "[winrt][unique_winrt_event_token]")
{
ComPtr<IMemoryBufferReference> testEventSender;
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
ComPtr<IClosable> closable;
testEventSender.As(&closable);
int timesInvoked = 0;
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
([&timesInvoked](IInspectable*, IInspectable*)
{
timesInvoked++;
return S_OK;
});
REQUIRE(timesInvoked == 0);
{
#ifdef WIL_ENABLE_EXCEPTIONS
auto exceptionPolicyToken = WI_MakeUniqueWinRtEventToken(Closed, testEventSender.Get(), handler.Get());
REQUIRE(static_cast<bool>(exceptionPolicyToken));
#endif
auto failFastPolicyToken = WI_MakeUniqueWinRtEventTokenFailFast(Closed, testEventSender.Get(), handler.Get());
REQUIRE(static_cast<bool>(failFastPolicyToken));
REQUIRE_SUCCEEDED(closable->Close());
#ifdef WIL_ENABLE_EXCEPTIONS
REQUIRE(timesInvoked == 2);
#else
REQUIRE(timesInvoked == 1);
#endif
}
REQUIRE_SUCCEEDED(closable->Close());
#ifdef WIL_ENABLE_EXCEPTIONS
REQUIRE(timesInvoked == 2);
#else
REQUIRE(timesInvoked == 1);
#endif
}

517
Externals/WIL/tests/WatcherTests.cpp vendored Normal file
View file

@ -0,0 +1,517 @@
#include <wil/filesystem.h>
#include <wil/registry.h>
#include <wil/resource.h>
#include <memory> // For shared_event_watcher
#include <wil/resource.h>
#include "common.h"
TEST_CASE("EventWatcherTests::Construction", "[resource][event_watcher]")
{
SECTION("Create unique_event_watcher_nothrow without event")
{
auto watcher = wil::make_event_watcher_nothrow([]{});
REQUIRE(watcher != nullptr);
}
SECTION("Create unique_event_watcher_nothrow with unique_event_nothrow")
{
wil::unique_event_nothrow eventToPass;
FAIL_FAST_IF_FAILED(eventToPass.create(wil::EventOptions::None));
auto watcher = wil::make_event_watcher_nothrow(wistd::move(eventToPass), []{});
REQUIRE(watcher != nullptr);
REQUIRE(eventToPass.get() == nullptr); // move construction must take it
}
SECTION("Create unique_event_watcher_nothrow with handle")
{
wil::unique_event_nothrow eventToDupe;
FAIL_FAST_IF_FAILED(eventToDupe.create(wil::EventOptions::None));
auto watcher = wil::make_event_watcher_nothrow(eventToDupe.get(), []{});
REQUIRE(watcher != nullptr);
REQUIRE(eventToDupe.get() != nullptr); // handle duped in this case
}
#ifdef WIL_ENABLE_EXCEPTIONS
SECTION("Create unique_event_watcher_nothrow with unique_event")
{
wil::unique_event eventToPass(wil::EventOptions::None);
auto watcher = wil::make_event_watcher_nothrow(wistd::move(eventToPass), []{});
REQUIRE(watcher != nullptr);
REQUIRE(eventToPass.get() == nullptr); // move construction must take it
}
SECTION("Create unique_event_watcher without event")
{
auto watcher = wil::make_event_watcher([]{});
}
SECTION("Create unique_event_watcher with unique_event_nothrow")
{
wil::unique_event_nothrow eventToPass;
THROW_IF_FAILED(eventToPass.create(wil::EventOptions::None));
auto watcher = wil::make_event_watcher(wistd::move(eventToPass), []{});
REQUIRE(eventToPass.get() == nullptr); // move construction must take it
}
SECTION("Create unique_event_watcher with unique_event")
{
wil::unique_event eventToPass(wil::EventOptions::None);
auto watcher = wil::make_event_watcher(wistd::move(eventToPass), []{});
REQUIRE(eventToPass.get() == nullptr); // move construction must take it
}
SECTION("Create unique_event_watcher with handle")
{
wil::unique_event eventToDupe(wil::EventOptions::None);
auto watcher = wil::make_event_watcher(eventToDupe.get(), []{});
REQUIRE(eventToDupe.get() != nullptr); // handle duped in this case
}
SECTION("Create unique_event_watcher shared watcher")
{
wil::shared_event_watcher sharedWatcher = wil::make_event_watcher([]{});
}
#endif
}
static auto make_event(wil::EventOptions options = wil::EventOptions::None)
{
wil::unique_event_nothrow result;
FAIL_FAST_IF_FAILED(result.create(options));
return result;
}
TEST_CASE("EventWatcherTests::VerifyDelivery", "[resource][event_watcher]")
{
auto notificationReceived = make_event();
int volatile countObserved = 0;
auto watcher = wil::make_event_watcher_nothrow([&]
{
countObserved++;
notificationReceived.SetEvent();
});
REQUIRE(watcher != nullptr);
watcher.SetEvent();
REQUIRE(notificationReceived.wait(5000)); // 5 second max wait
watcher.SetEvent();
REQUIRE(notificationReceived.wait(5000)); // 5 second max wait
REQUIRE(countObserved == 2);
}
TEST_CASE("EventWatcherTests::VerifyLastChangeObserved", "[resource][event_watcher]")
{
wil::EventOptions const eventOptions[] =
{
wil::EventOptions::None,
wil::EventOptions::ManualReset,
wil::EventOptions::Signaled,
wil::EventOptions::ManualReset | wil::EventOptions::Signaled,
};
for (auto const &eventOption : eventOptions)
{
auto allChangesMade = make_event(wil::EventOptions::ManualReset); // ManualReset to avoid hang in case where 2 callbacks are generated (a test failure).
auto processedChange = make_event();
DWORD volatile stateToObserve = 0;
DWORD volatile lastObservedState = 0;
int volatile countObserved = 0;
auto watcher = wil::make_event_watcher_nothrow(make_event(eventOption), [&]
{
allChangesMade.wait();
countObserved++;
lastObservedState = stateToObserve;
processedChange.SetEvent();
});
REQUIRE(watcher != nullptr);
stateToObserve = 1;
watcher.SetEvent();
stateToObserve = 2;
watcher.SetEvent();
allChangesMade.SetEvent();
REQUIRE(processedChange.wait(5000));
REQUIRE((countObserved == 1 || countObserved == 2)); // ensure the race worked how we wanted it to
REQUIRE(lastObservedState == stateToObserve);
}
}
#define ROOT_KEY_PAIR HKEY_CURRENT_USER, L"Software\\Microsoft\\RegistryWatcherTest"
TEST_CASE("RegistryWatcherTests::Construction", "[registry][registry_watcher]")
{
SECTION("Create unique_registry_watcher_nothrow with string")
{
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind){});
REQUIRE(watcher);
}
SECTION("Create unique_registry_watcher_nothrow with unique_hkey")
{
wil::unique_hkey keyToMove;
REQUIRE_SUCCEEDED(HRESULT_FROM_WIN32(RegCreateKeyExW(ROOT_KEY_PAIR, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToMove, nullptr)));
auto watcher = wil::make_registry_watcher_nothrow(wistd::move(keyToMove), true, [&](wil::RegistryChangeKind){});
REQUIRE(watcher);
REQUIRE(keyToMove.get() == nullptr); // ownership is transferred
}
SECTION("Create unique_registry_watcher_nothrow with handle")
{
// construct with just an open registry key
wil::unique_hkey rootKey;
REQUIRE_SUCCEEDED(HRESULT_FROM_WIN32(RegCreateKeyExW(ROOT_KEY_PAIR, 0, nullptr, 0, KEY_NOTIFY, nullptr, &rootKey, nullptr)));
auto watcher = wil::make_registry_watcher_nothrow(rootKey.get(), L"", true, [&](wil::RegistryChangeKind){});
REQUIRE(watcher);
}
#ifdef WIL_ENABLE_EXCEPTIONS
SECTION("Create unique_registry_watcher with string")
{
REQUIRE_NOTHROW(wil::make_registry_watcher(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind){}));
}
SECTION("Create unique_registry_watcher with unique_hkey")
{
wil::unique_hkey keyToMove;
THROW_IF_FAILED(HRESULT_FROM_WIN32(RegCreateKeyExW(ROOT_KEY_PAIR, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToMove, nullptr)));
REQUIRE_NOTHROW(wil::make_registry_watcher(wistd::move(keyToMove), true, [&](wil::RegistryChangeKind){}));
REQUIRE(keyToMove.get() == nullptr); // ownership is transferred
}
#endif
}
void SetRegistryValue(
_In_ HKEY hKey,
_In_opt_ LPCWSTR lpSubKey,
_In_opt_ LPCWSTR lpValueName,
_In_ DWORD dwType,
_In_reads_bytes_opt_(cbData) LPCVOID lpData,
_In_ DWORD cbData)
{
wil::unique_hkey key;
REQUIRE(RegOpenKeyExW(hKey, lpSubKey, 0, KEY_WRITE, &key) == ERROR_SUCCESS);
REQUIRE(RegSetValueExW(key.get(), lpValueName, 0, dwType, static_cast<BYTE const*>(lpData), cbData) == ERROR_SUCCESS);
}
TEST_CASE("RegistryWatcherTests::VerifyDelivery", "[registry][registry_watcher]")
{
RegDeleteTreeW(ROOT_KEY_PAIR); // So that we get the 'Modify' event
auto notificationReceived = make_event();
int volatile countObserved = 0;
auto volatile observedChangeType = wil::RegistryChangeKind::Delete;
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
{
countObserved++;
observedChangeType = changeType;
notificationReceived.SetEvent();
});
REQUIRE(watcher);
DWORD value = 1;
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
REQUIRE(notificationReceived.wait(5000));
REQUIRE(observedChangeType == wil::RegistryChangeKind::Modify);
value++;
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
REQUIRE(notificationReceived.wait(5000));
REQUIRE(countObserved == 2);
REQUIRE(observedChangeType == wil::RegistryChangeKind::Modify);
}
TEST_CASE("RegistryWatcherTests::VerifyLastChangeObserved", "[registry][registry_watcher]")
{
RegDeleteTreeW(ROOT_KEY_PAIR);
auto allChangesMade = make_event(wil::EventOptions::ManualReset); // ManualReset for the case where both registry operations result in a callback.
auto processedChange = make_event();
DWORD volatile stateToObserve = 0;
DWORD volatile lastObservedState = 0;
DWORD volatile lastObservedValue = 0;
int volatile countObserved = 0;
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&, called = false](wil::RegistryChangeKind) mutable
{
// This callback may be called more than once (since we modify the key twice), but we're holding references to
// local variables. Therefore, bail out if this is not the first time we're called
if (called)
{
return;
}
called = true;
allChangesMade.wait();
countObserved++;
lastObservedState = stateToObserve;
DWORD value, cbValue = sizeof(value);
RegGetValueW(ROOT_KEY_PAIR, L"value", RRF_RT_REG_DWORD, nullptr, &value, &cbValue);
lastObservedValue = value;
processedChange.SetEvent();
});
REQUIRE(watcher);
DWORD value;
// make 2 changes and verify that only the last gets observed
stateToObserve = 1;
value = 0;
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
stateToObserve = 2;
value = 1;
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
allChangesMade.SetEvent();
REQUIRE(processedChange.wait(5000));
REQUIRE(countObserved >= 1); // Sometimes 2 events are observed, see if this can be eliminated.
REQUIRE(lastObservedState == stateToObserve);
REQUIRE(lastObservedValue == 1);
}
TEST_CASE("RegistryWatcherTests::VerifyDeleteBehavior", "[registry][registry_watcher]")
{
auto notificationReceived = make_event();
int volatile countObserved = 0;
auto volatile observedChangeType = wil::RegistryChangeKind::Modify;
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
{
countObserved++;
observedChangeType = changeType;
notificationReceived.SetEvent();
});
REQUIRE(watcher);
RegDeleteTreeW(ROOT_KEY_PAIR); // delete the key to signal the watcher with the special error case
REQUIRE(notificationReceived.wait(5000));
REQUIRE(countObserved == 1);
REQUIRE(observedChangeType == wil::RegistryChangeKind::Delete);
}
TEST_CASE("RegistryWatcherTests::VerifyResetInCallback", "[registry][registry_watcher]")
{
auto notificationReceived = make_event();
wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, TRUE, [&](wil::RegistryChangeKind)
{
watcher.reset();
DWORD value = 2;
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
notificationReceived.SetEvent();
});
REQUIRE(watcher);
DWORD value = 1;
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
REQUIRE(notificationReceived.wait(5000));
}
// Stress test, disabled by default
TEST_CASE("RegistryWatcherTests::VerifyResetInCallbackStress", "[!hide][registry][registry_watcher][stress]")
{
for (DWORD value = 0; value < 10000; ++value)
{
wil::srwlock lock;
auto notificationReceived = make_event();
wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, TRUE, [&](wil::RegistryChangeKind)
{
{
auto al = lock.lock_exclusive();
watcher.reset(); // get m_refCount to 1 to ensure the Release happens on the background thread
}
++value;
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
notificationReceived.SetEvent();
});
REQUIRE(watcher);
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
notificationReceived.wait();
{
auto al = lock.lock_exclusive();
watcher.reset();
}
}
}
TEST_CASE("RegistryWatcherTests::VerifyResetAfterDelete", "[registry][registry_watcher]")
{
auto notificationReceived = make_event();
int volatile countObserved = 0;
auto volatile observedChangeType = wil::RegistryChangeKind::Modify;
wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
{
countObserved++;
observedChangeType = changeType;
notificationReceived.SetEvent();
watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
{
countObserved++;
observedChangeType = changeType;
notificationReceived.SetEvent();
});
REQUIRE(watcher);
});
REQUIRE(watcher);
RegDeleteTreeW(ROOT_KEY_PAIR); // delete the key to signal the watcher with the special error case
notificationReceived.wait();
REQUIRE(countObserved == 1);
REQUIRE(observedChangeType == wil::RegistryChangeKind::Delete);
// wait for the reset to finish. The constructor creates the registry key
notificationReceived.wait(300);
DWORD value = 1;
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
notificationReceived.wait();
REQUIRE(countObserved == 2);
REQUIRE(observedChangeType == wil::RegistryChangeKind::Modify);
}
TEST_CASE("RegistryWatcherTests::VerifyCallbackFinishesBeforeFreed", "[registry][registry_watcher]")
{
auto notificationReceived = make_event();
auto deleteNotification = make_event();
int volatile deleteObserved = 0;
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind)
{
notificationReceived.SetEvent();
// ensure that the callback is still being executed while the watcher is reset().
deleteNotification.wait(200);
deleteObserved++;
notificationReceived.SetEvent();
});
RegDeleteTreeW(ROOT_KEY_PAIR); // delete the key to signal the watcher with the special error case
REQUIRE(notificationReceived.wait(5000));
watcher.reset();
deleteNotification.SetEvent();
REQUIRE(notificationReceived.wait(5000));
REQUIRE(deleteObserved == 1);
}
TEST_CASE("FileSystemWatcherTests::Construction", "[resource][folder_watcher]")
{
SECTION("Create unique_folder_watcher_nothrow with valid path")
{
auto watcher = wil::make_folder_watcher_nothrow(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, []{});
REQUIRE(watcher);
}
SECTION("Create unique_folder_watcher_nothrow with invalid path")
{
auto watcher = wil::make_folder_watcher_nothrow(L"X:\\invalid path", true, wil::FolderChangeEvents::All, []{});
REQUIRE(!watcher);
}
#ifdef WIL_ENABLE_EXCEPTIONS
SECTION("Create unique_folder_watcher with valid path")
{
REQUIRE_NOTHROW(wil::make_folder_watcher(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, []{}));
}
SECTION("Create unique_folder_watcher with invalid path")
{
REQUIRE_THROWS(wil::make_folder_watcher(L"X:\\invalid path", true, wil::FolderChangeEvents::All, []{}));
}
#endif
}
TEST_CASE("FileSystemWatcherTests::VerifyDelivery", "[resource][folder_watcher]")
{
witest::TestFolder folder;
REQUIRE(folder);
auto notificationEvent = make_event();
int observedCount = 0;
auto watcher = wil::make_folder_watcher_nothrow(folder.Path(), true, wil::FolderChangeEvents::All, [&]
{
++observedCount;
notificationEvent.SetEvent();
});
REQUIRE(watcher);
witest::TestFile file(folder.Path(), L"file.txt");
REQUIRE(file);
REQUIRE(notificationEvent.wait(5000));
REQUIRE(observedCount == 1);
witest::TestFile file2(folder.Path(), L"file2.txt");
REQUIRE(file2);
REQUIRE(notificationEvent.wait(5000));
REQUIRE(observedCount == 2);
}
TEST_CASE("FolderChangeReaderTests::Construction", "[resource][folder_change_reader]")
{
SECTION("Create folder_change_reader_nothrow with valid path")
{
auto reader = wil::make_folder_change_reader_nothrow(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, [](auto, auto) {});
REQUIRE(reader);
}
SECTION("Create folder_change_reader_nothrow with invalid path")
{
auto reader = wil::make_folder_change_reader_nothrow(L"X:\\invalid path", true, wil::FolderChangeEvents::All, [](auto, auto) {});
REQUIRE(!reader);
}
#ifdef WIL_ENABLE_EXCEPTIONS
SECTION("Create folder_change_reader with valid path")
{
REQUIRE_NOTHROW(wil::make_folder_change_reader(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, [](auto, auto) {}));
}
SECTION("Create folder_change_reader with invalid path")
{
REQUIRE_THROWS(wil::make_folder_change_reader(L"X:\\invalid path", true, wil::FolderChangeEvents::All, [](auto, auto) {}));
}
#endif
}
TEST_CASE("FolderChangeReaderTests::VerifyDelivery", "[resource][folder_change_reader]")
{
witest::TestFolder folder;
REQUIRE(folder);
auto notificationEvent = make_event();
wil::FolderChangeEvent observedEvent;
wchar_t observedFileName[MAX_PATH] = L"";
auto reader = wil::make_folder_change_reader_nothrow(folder.Path(), true, wil::FolderChangeEvents::All,
[&](wil::FolderChangeEvent event, PCWSTR fileName)
{
observedEvent = event;
StringCchCopyW(observedFileName, ARRAYSIZE(observedFileName), fileName);
notificationEvent.SetEvent();
});
REQUIRE(reader);
witest::TestFile testFile(folder.Path(), L"file.txt");
REQUIRE(testFile);
REQUIRE(notificationEvent.wait(5000));
REQUIRE(observedEvent == wil::FolderChangeEvent::Added);
REQUIRE(wcscmp(observedFileName, L"file.txt") == 0);
witest::TestFile testFile2(folder.Path(), L"file2.txt");
REQUIRE(testFile2);
REQUIRE(notificationEvent.wait(5000));
REQUIRE(observedEvent == wil::FolderChangeEvent::Added);
REQUIRE(wcscmp(observedFileName, L"file2.txt") == 0);
}

894
Externals/WIL/tests/WinRTTests.cpp vendored Normal file
View file

@ -0,0 +1,894 @@
#include <time.h> // TODO: https://github.com/microsoft/wil/issues/44
#include <wil/winrt.h>
#ifdef WIL_ENABLE_EXCEPTIONS
#include <map>
#include <string>
#endif
// Required for pinterface template specializations that we depend on in this test
#include <Windows.ApplicationModel.Chat.h>
#pragma push_macro("GetCurrentTime")
#undef GetCurrentTime
#include <Windows.UI.Xaml.Data.h>
#pragma pop_macro("GetCurrentTime")
#include "common.h"
#include "FakeWinRTTypes.h"
#include "test_objects.h"
using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::Foundation::Collections;
using namespace ABI::Windows::Storage;
using namespace ABI::Windows::System;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
TEST_CASE("WinRTTests::VerifyTraitsTypes", "[winrt]")
{
static_assert(wistd::is_same_v<bool, typename wil::details::LastType<int, bool>::type>, "");
static_assert(wistd::is_same_v<int, typename wil::details::LastType<int>::type>, "");
static_assert(wistd::is_same_v<IAsyncAction*, decltype(wil::details::GetReturnParamPointerType(&IFileIOStatics::WriteTextAsync))>, "");
static_assert(wistd::is_same_v<IAsyncOperation<bool>*, decltype(wil::details::GetReturnParamPointerType(&ILauncherStatics::LaunchUriAsync))>, "");
static_assert(wistd::is_same_v<void, decltype(wil::details::GetAsyncResultType(static_cast<IAsyncAction*>(nullptr)))>, "");
static_assert(wistd::is_same_v<boolean, decltype(wil::details::GetAsyncResultType(static_cast<IAsyncOperation<bool>*>(nullptr)))>, "");
static_assert(wistd::is_same_v<IStorageFile*, decltype(wil::details::GetAsyncResultType(static_cast<IAsyncOperation<StorageFile*>*>(nullptr)))>, "");
}
template <bool InhibitArrayReferences, bool IgnoreCase, typename LhsT, typename RhsT>
void DoHStringComparisonTest(LhsT&& lhs, RhsT&& rhs, int relation)
{
using compare = wil::details::hstring_compare<InhibitArrayReferences, IgnoreCase>;
// == and !=
REQUIRE(compare::equals(lhs, rhs) == (relation == 0));
REQUIRE(compare::not_equals(lhs, rhs) == (relation != 0));
REQUIRE(compare::equals(rhs, lhs) == (relation == 0));
REQUIRE(compare::not_equals(rhs, lhs) == (relation != 0));
// < and >=
REQUIRE(compare::less(lhs, rhs) == (relation < 0));
REQUIRE(compare::greater_equals(lhs, rhs) == (relation >= 0));
REQUIRE(compare::less(rhs, lhs) == (relation > 0));
REQUIRE(compare::greater_equals(rhs, lhs) == (relation <= 0));
// > and <=
REQUIRE(compare::greater(lhs, rhs) == (relation > 0));
REQUIRE(compare::less_equals(lhs, rhs) == (relation <= 0));
REQUIRE(compare::greater(rhs, lhs) == (relation < 0));
REQUIRE(compare::less_equals(rhs, lhs) == (relation >= 0));
// We wish to test with both const and non-const values. We can do this for free here so long as the type is
// not an array since changing the const-ness of an array may change the expected results
#pragma warning(suppress: 4127)
if (!wistd::is_array<wistd::remove_reference_t<LhsT>>::value &&
!wistd::is_const<wistd::remove_reference_t<LhsT>>::value)
{
const wistd::remove_reference_t<LhsT>& constLhs = lhs;
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(constLhs, rhs, relation);
}
#pragma warning(suppress: 4127)
if (!wistd::is_array<wistd::remove_reference_t<RhsT>>::value &&
!wistd::is_const<wistd::remove_reference_t<RhsT>>::value)
{
const wistd::remove_reference_t<RhsT>& constRhs = rhs;
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, constRhs, relation);
}
}
// The two string arguments are expected to compare equal to one another using the specified IgnoreCase argument and
// contain at least one embedded null character
template <bool InhibitArrayReferences, bool IgnoreCase, size_t Size>
void DoHStringSameValueComparisonTest(const wchar_t (&lhs)[Size], const wchar_t (&rhs)[Size])
{
wchar_t lhsNonConstArray[Size + 5];
wchar_t rhsNonConstArray[Size + 5];
wcsncpy_s(lhsNonConstArray, lhs, Size);
wcsncpy_s(rhsNonConstArray, rhs, Size);
// For non-const arrays, we should never deduce length, so even though we append different values to each string, we
// do so after the last null character, so they should never be read
wcsncpy_s(lhsNonConstArray + Size + 1, 4, L"foo", 3);
wcsncpy_s(rhsNonConstArray + Size + 1, 4, L"bar", 3);
const wchar_t* lhsCstr = lhs;
const wchar_t* rhsCstr = rhs;
HStringReference lhsRef(lhs);
HStringReference rhsRef(rhs);
HString lhsStr;
HString rhsStr;
REQUIRE_SUCCEEDED(lhsStr.Set(lhs));
REQUIRE_SUCCEEDED(rhsStr.Set(rhs));
auto lhsHstr = lhsStr.Get();
auto rhsHstr = rhsStr.Get();
wil::unique_hstring lhsUniqueStr;
wil::unique_hstring rhsUniqueStr;
REQUIRE_SUCCEEDED(lhsStr.CopyTo(&lhsUniqueStr));
REQUIRE_SUCCEEDED(rhsStr.CopyTo(&rhsUniqueStr));
// Const array - embedded nulls are included only if InhibitArrayReferences is false
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhs, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsNonConstArray, InhibitArrayReferences ? 0 : 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsCstr, InhibitArrayReferences ? 0 : 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsRef, InhibitArrayReferences ? -1 : 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsStr, InhibitArrayReferences ? -1 : 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsHstr, InhibitArrayReferences ? -1 : 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsUniqueStr, InhibitArrayReferences ? -1 : 0);
// Non-const array - *never* deduce length
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsNonConstArray, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsCstr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsRef, -1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsStr, -1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsHstr, -1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsUniqueStr, -1);
// C string - impossible to deduce length
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsCstr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsRef, -1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsStr, -1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsHstr, -1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsUniqueStr, -1);
// HStringReference
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsRef, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsStr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsHstr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsUniqueStr, 0);
// HString
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsStr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsHstr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsUniqueStr, 0);
// Raw HSTRING
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsHstr, rhsHstr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsHstr, rhsUniqueStr, 0);
// wil::unique_hstring
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsUniqueStr, rhsUniqueStr, 0);
#ifdef WIL_ENABLE_EXCEPTIONS
std::wstring lhsWstr(lhs, 7);
std::wstring rhsWstr(rhs, 7);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsWstr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhs, InhibitArrayReferences ? 1 : 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsNonConstArray, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsCstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsRef, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsStr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsHstr, 0);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsUniqueStr, 0);
#endif
}
// It's expected that the first argument (lhs) compares greater than the second argument (rhs)
template <bool InhibitArrayReferences, bool IgnoreCase, size_t LhsSize, size_t RhsSize>
void DoHStringDifferentValueComparisonTest(const wchar_t (&lhs)[LhsSize], const wchar_t (&rhs)[RhsSize])
{
wchar_t lhsNonConstArray[LhsSize];
wchar_t rhsNonConstArray[RhsSize];
wcsncpy_s(lhsNonConstArray, lhs, LhsSize);
wcsncpy_s(rhsNonConstArray, rhs, RhsSize);
const wchar_t* lhsCstr = lhs;
const wchar_t* rhsCstr = rhs;
HStringReference lhsRef(lhs);
HStringReference rhsRef(rhs);
HString lhsStr;
HString rhsStr;
REQUIRE_SUCCEEDED(lhsStr.Set(lhs));
REQUIRE_SUCCEEDED(rhsStr.Set(rhs));
auto lhsHstr = lhsStr.Get();
auto rhsHstr = rhsStr.Get();
wil::unique_hstring lhsUniqueStr;
wil::unique_hstring rhsUniqueStr;
REQUIRE_SUCCEEDED(lhsStr.CopyTo(&lhsUniqueStr));
REQUIRE_SUCCEEDED(rhsStr.CopyTo(&rhsUniqueStr));
// Const array
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhs, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsNonConstArray, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsCstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsRef, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsStr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsHstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsUniqueStr, 1);
// Non-const array
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsNonConstArray, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsCstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsRef, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsStr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsHstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsUniqueStr, 1);
// C string
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsCstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsRef, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsStr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsHstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsUniqueStr, 1);
// HStringReference
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsRef, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsStr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsHstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsUniqueStr, 1);
// HString
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsStr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsHstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsUniqueStr, 1);
// Raw HSTRING
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsHstr, rhsHstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsHstr, rhsUniqueStr, 1);
// wil::unique_hstring
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsUniqueStr, rhsUniqueStr, 1);
#ifdef WIL_ENABLE_EXCEPTIONS
std::wstring lhsWstr(lhs, 7);
std::wstring rhsWstr(rhs, 7);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsWstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhs, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsNonConstArray, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsCstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsRef, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsStr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsHstr, 1);
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsUniqueStr, 1);
#endif
}
TEST_CASE("WinRTTests::HStringComparison", "[winrt][hstring_compare]")
{
SECTION("Don't inhibit arrays")
{
DoHStringSameValueComparisonTest<false, false>(L"foo\0bar", L"foo\0bar");
DoHStringDifferentValueComparisonTest<false, false>(L"foo", L"bar");
}
SECTION("Inhibit arrays")
{
DoHStringSameValueComparisonTest<true, false>(L"foo\0bar", L"foo\0bar");
DoHStringDifferentValueComparisonTest<true, false>(L"foo", L"bar");
}
SECTION("Ignore case")
{
DoHStringSameValueComparisonTest<true, true>(L"foo\0bar", L"FoO\0bAR");
DoHStringDifferentValueComparisonTest<true, true>(L"Foo", L"baR");
}
SECTION("Empty string")
{
const wchar_t constArray[] = L"";
wchar_t nonConstArray[] = L"";
const wchar_t* cstr = constArray;
const wchar_t* nullCstr = nullptr;
// str may end up referencing a null HSTRING. That's fine; we'll just test null HSTRING twice
HString str;
REQUIRE_SUCCEEDED(str.Set(constArray));
HSTRING nullHstr = nullptr;
// Const array - impossible to use null value
DoHStringComparisonTest<false, false>(constArray, constArray, 0);
DoHStringComparisonTest<false, false>(constArray, nonConstArray, 0);
DoHStringComparisonTest<false, false>(constArray, cstr, 0);
DoHStringComparisonTest<false, false>(constArray, nullCstr, 0);
DoHStringComparisonTest<false, false>(constArray, str.Get(), 0);
DoHStringComparisonTest<false, false>(constArray, nullHstr, 0);
// Non-const array - impossible to use null value
DoHStringComparisonTest<false, false>(nonConstArray, nonConstArray, 0);
DoHStringComparisonTest<false, false>(nonConstArray, cstr, 0);
DoHStringComparisonTest<false, false>(nonConstArray, nullCstr, 0);
DoHStringComparisonTest<false, false>(nonConstArray, str.Get(), 0);
DoHStringComparisonTest<false, false>(nonConstArray, nullHstr, 0);
// Non-null c-string
DoHStringComparisonTest<false, false>(cstr, cstr, 0);
DoHStringComparisonTest<false, false>(cstr, nullCstr, 0);
DoHStringComparisonTest<false, false>(cstr, str.Get(), 0);
DoHStringComparisonTest<false, false>(cstr, nullHstr, 0);
// Null c-string
DoHStringComparisonTest<false, false>(nullCstr, nullCstr, 0);
DoHStringComparisonTest<false, false>(nullCstr, str.Get(), 0);
DoHStringComparisonTest<false, false>(nullCstr, nullHstr, 0);
// (Possibly) non-null HSTRING
DoHStringComparisonTest<false, false>(str.Get(), str.Get(), 0);
DoHStringComparisonTest<false, false>(str.Get(), nullHstr, 0);
// Null HSTRING
DoHStringComparisonTest<false, false>(nullHstr, nullHstr, 0);
#ifdef WIL_ENABLE_EXCEPTIONS
std::wstring wstr;
DoHStringComparisonTest<false, false>(wstr, wstr, 0);
DoHStringComparisonTest<false, false>(wstr, constArray, 0);
DoHStringComparisonTest<false, false>(wstr, nonConstArray, 0);
DoHStringComparisonTest<false, false>(wstr, cstr, 0);
DoHStringComparisonTest<false, false>(wstr, nullCstr, 0);
DoHStringComparisonTest<false, false>(wstr, str.Get(), 0);
DoHStringComparisonTest<false, false>(wstr, nullHstr, 0);
#endif
}
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("WinRTTests::HStringMapTest", "[winrt][hstring_compare]")
{
int nextValue = 0;
std::map<std::wstring, int> wstringMap;
wstringMap.emplace(L"foo", nextValue++);
wstringMap.emplace(L"bar", nextValue++);
wstringMap.emplace(std::wstring(L"foo\0bar", 7), nextValue++);
wstringMap.emplace(L"adding", nextValue++);
wstringMap.emplace(L"quite", nextValue++);
wstringMap.emplace(L"a", nextValue++);
wstringMap.emplace(L"few", nextValue++);
wstringMap.emplace(L"more", nextValue++);
wstringMap.emplace(L"values", nextValue++);
wstringMap.emplace(L"for", nextValue++);
wstringMap.emplace(L"testing", nextValue++);
wstringMap.emplace(L"", nextValue++);
std::map<HString, int> hstringMap;
for (auto& pair : wstringMap)
{
HString str;
THROW_IF_FAILED(str.Set(pair.first.c_str(), static_cast<UINT>(pair.first.length())));
hstringMap.emplace(std::move(str), pair.second);
}
// Order should be the same as the map of wstring
auto itr = hstringMap.begin();
for (auto& pair : wstringMap)
{
REQUIRE(itr != hstringMap.end());
REQUIRE(itr->first == HStringReference(pair.first.c_str(), static_cast<UINT>(pair.first.length())));
// Should also be able to find the value
REQUIRE(hstringMap.find(pair.first) != hstringMap.end());
++itr;
}
REQUIRE(itr == hstringMap.end());
const wchar_t constArray[] = L"foo\0bar";
wchar_t nonConstArray[] = L"foo\0bar";
const wchar_t* cstr = constArray;
HString key;
wil::unique_hstring uniqueHstr;
THROW_IF_FAILED(key.Set(constArray));
THROW_IF_FAILED(key.CopyTo(&uniqueHstr));
HStringReference ref(constArray);
std::wstring wstr(constArray, 7);
auto verifyFunc = [&](int expectedValue, auto&& keyValue)
{
auto itr = hstringMap.find(std::forward<decltype(keyValue)>(keyValue));
REQUIRE(itr != hstringMap.end());
REQUIRE(expectedValue == itr->second);
};
// The following should find "foo\0bar"
auto expectedValue = wstringMap[wstr];
verifyFunc(expectedValue, uniqueHstr);
verifyFunc(expectedValue, key);
verifyFunc(expectedValue, key.Get());
verifyFunc(expectedValue, ref);
verifyFunc(expectedValue, wstr);
// Arrays/strings should not deduce length and should therefore find "foo"
expectedValue = wstringMap[L"foo"];
verifyFunc(expectedValue, constArray);
verifyFunc(expectedValue, nonConstArray);
verifyFunc(expectedValue, cstr);
// Should not ignore case
REQUIRE(hstringMap.find(L"FOO") == hstringMap.end());
// Should also be able to find empty values
const wchar_t constEmptyArray[] = L"";
wchar_t nonConstEmptyArray[] = L"";
const wchar_t* emptyCstr = constEmptyArray;
const wchar_t* nullCstr = nullptr;
HString emptyStr;
HSTRING nullHstr = nullptr;
std::wstring emptyWstr;
expectedValue = wstringMap[L""];
verifyFunc(expectedValue, constEmptyArray);
verifyFunc(expectedValue, nonConstEmptyArray);
verifyFunc(expectedValue, emptyCstr);
verifyFunc(expectedValue, nullCstr);
verifyFunc(expectedValue, emptyStr);
verifyFunc(expectedValue, nullHstr);
verifyFunc(expectedValue, emptyWstr);
}
TEST_CASE("WinRTTests::HStringCaseInsensitiveMapTest", "[winrt][hstring_compare]")
{
std::map<HString, int, wil::hstring_insensitive_less> hstringMap;
auto emplaceFunc = [&](auto&& key, int value)
{
HString str;
THROW_IF_FAILED(str.Set(std::forward<decltype(key)>(key)));
hstringMap.emplace(std::move(str), value);
};
int nextValue = 0;
int fooValue = nextValue++;
emplaceFunc(L"foo", fooValue);
emplaceFunc(L"bar", nextValue++);
int foobarValue = nextValue++;
emplaceFunc(L"foo\0bar", foobarValue);
emplaceFunc(L"foobar", nextValue++);
emplaceFunc(L"adding", nextValue++);
emplaceFunc(L"some", nextValue++);
emplaceFunc(L"more", nextValue++);
emplaceFunc(L"values", nextValue++);
emplaceFunc(L"for", nextValue++);
emplaceFunc(L"testing", nextValue++);
WI_ASSERT(static_cast<size_t>(nextValue) == hstringMap.size());
const wchar_t constArray[] = L"FoO\0BAr";
wchar_t nonConstArray[] = L"fOo\0baR";
const wchar_t* cstr = constArray;
HString key;
wil::unique_hstring uniqueHstr;
THROW_IF_FAILED(key.Set(constArray));
THROW_IF_FAILED(key.CopyTo(&uniqueHstr));
HStringReference ref(constArray);
std::wstring wstr(constArray, 7);
auto verifyFunc = [&](int expectedValue, auto&& key)
{
auto itr = hstringMap.find(std::forward<decltype(key)>(key));
REQUIRE(itr != std::end(hstringMap));
REQUIRE(expectedValue == itr->second);
};
// The following should find "foo\0bar"
verifyFunc(foobarValue, uniqueHstr);
verifyFunc(foobarValue, key);
verifyFunc(foobarValue, key.Get());
verifyFunc(foobarValue, ref);
verifyFunc(foobarValue, wstr);
// Arrays/strings should not deduce length and should therefore find "foo"
verifyFunc(fooValue, constArray);
verifyFunc(fooValue, nonConstArray);
verifyFunc(fooValue, cstr);
}
#endif
// This is not a test method, nor should it be called. This is a compilation-only test.
#ifdef WIL_ENABLE_EXCEPTIONS
void RunWhenCompleteCompilationTest()
{
{
ComPtr<IAsyncOperation<HSTRING>> stringOp;
wil::run_when_complete(stringOp.Get(), [](HRESULT /* result */, HSTRING /* value */) {});
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
auto result = wil::wait_for_completion(stringOp.Get());
#endif
}
{
ComPtr<IAsyncOperationWithProgress<HSTRING, UINT64>> stringOpWithProgress;
wil::run_when_complete(stringOpWithProgress.Get(), [](HRESULT /* result */, HSTRING /* value */) {});
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
auto result = wil::wait_for_completion(stringOpWithProgress.Get());
#endif
}
}
#endif
TEST_CASE("WinRTTests::RunWhenCompleteMoveOnlyTest", "[winrt][run_when_complete]")
{
auto op = Make<FakeAsyncOperation<int>>();
REQUIRE(op);
bool gotEvent = false;
auto hr = wil::run_when_complete_nothrow(op.Get(), [&gotEvent, enforce = cannot_copy{}](HRESULT hr, int result)
{
(void)enforce;
REQUIRE_SUCCEEDED(hr);
REQUIRE(result == 42);
gotEvent = true;
return S_OK;
});
REQUIRE_SUCCEEDED(hr);
op->Complete(S_OK, 42);
REQUIRE(gotEvent);
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
TEST_CASE("WinRTTests::WaitForCompletionTimeout", "[winrt][wait_for_completion]")
{
auto op = Make<FakeAsyncOperation<bool, boolean>>();
REQUIRE(op);
// The wait_for_completion* functions don't properly deduce the "decayed" async type, so force it here
auto asyncOp = static_cast<IAsyncOperation<bool>*>(op.Get());
bool timedOut = false;
REQUIRE_SUCCEEDED(wil::wait_for_completion_or_timeout_nothrow(asyncOp, 1, &timedOut));
REQUIRE(timedOut);
}
// This is not a test method, nor should it be called. This is a compilation-only test.
#pragma warning(push)
#pragma warning(disable: 4702) // Unreachable code
void WaitForCompletionCompilationTest()
{
// Ensure the wait_for_completion variants compile
FAIL_FAST_HR_MSG(E_UNEXPECTED, "This is a compilation test, and should not be called");
// template <typename TAsync = ABI::Windows::Foundation::IAsyncAction>
// inline HRESULT wait_for_completion_nothrow(_In_ TAsync* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
IAsyncAction* action = nullptr;
wil::wait_for_completion_nothrow(action);
wil::wait_for_completion_nothrow(action, COWAIT_DEFAULT);
// template <typename TResult>
// HRESULT wait_for_completion_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperation<TResult>* operation,
// _Out_ typename wil::details::MapAsyncOpResultType<TResult>::type* result,
// COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
IAsyncOperation<bool>* operation = nullptr;
wil::wait_for_completion_nothrow(operation);
wil::wait_for_completion_nothrow(operation, COWAIT_DEFAULT);
// template <typename TResult, typename TProgress>
// HRESULT wait_for_completion_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>* operation,
// _Out_ typename wil::details::MapAsyncOpProgressResultType<TResult, TProgress>::type* result,
// COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
ComPtr<IAsyncOperation<bool>> operationWithResult;
boolean result = false;
wil::wait_for_completion_nothrow(operationWithResult.Get(), &result);
wil::wait_for_completion_nothrow(operationWithResult.Get(), &result, COWAIT_DEFAULT);
DWORD timeoutValue = 1000; // arbitrary
bool timedOut = false;
// template <typename TAsync = ABI::Windows::Foundation::IAsyncAction>
// inline HRESULT wait_for_completion_or_timeout_nothrow(_In_ TAsync* operation,
// DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS);
wil::wait_for_completion_or_timeout_nothrow(action, timeoutValue, &timedOut);
wil::wait_for_completion_or_timeout_nothrow(action, timeoutValue, &timedOut, COWAIT_DEFAULT);
// template <typename TResult>
// HRESULT wait_for_completion_or_timeout_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperation<TResult>* operation,
// _Out_ typename wil::details::MapAsyncOpResultType<TResult>::type* result,
// DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS);
wil::wait_for_completion_or_timeout_nothrow(operation, timeoutValue, &timedOut);
wil::wait_for_completion_or_timeout_nothrow(operation, timeoutValue, &timedOut, COWAIT_DEFAULT);
// template <typename TResult, typename TProgress>
// HRESULT wait_for_completion_or_timeout_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>* operation,
// _Out_ typename wil::details::MapAsyncOpProgressResultType<TResult, TProgress>::type* result,
// DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS);
wil::wait_for_completion_or_timeout_nothrow(operationWithResult.Get(), &result, timeoutValue, &timedOut);
wil::wait_for_completion_or_timeout_nothrow(operationWithResult.Get(), &result, timeoutValue, &timedOut, COWAIT_DEFAULT);
#ifdef WIL_ENABLE_EXCEPTIONS
// template <typename TAsync = ABI::Windows::Foundation::IAsyncAction>
// inline void wait_for_completion(_In_ TAsync* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
wil::wait_for_completion(action);
wil::wait_for_completion(action, COWAIT_DEFAULT);
// template <typename TResult, typename TReturn = typename wil::details::MapToSmartType<typename wil::details::MapAsyncOpResultType<TResult>::type>::type>
// TReturn
// wait_for_completion(_In_ ABI::Windows::Foundation::IAsyncOperation<TResult>* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
wil::wait_for_completion(operation);
wil::wait_for_completion(operation, COWAIT_DEFAULT);
// template <typename TResult, typename TProgress, typename TReturn = typename wil::details::MapToSmartType<typename wil::details::MapAsyncOpResultType<TResult>::type>::type>
// TReturn
// wait_for_completion(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
result = wil::wait_for_completion(operationWithResult.Get());
result = wil::wait_for_completion(operationWithResult.Get(), COWAIT_DEFAULT);
#endif
}
#pragma warning(pop)
#endif
TEST_CASE("WinRTTests::TimeTTests", "[winrt][time_t]")
{
// Verifying that converting DateTime variable set as the date that means 0 as time_t works
DateTime time1 = { wil::SecondsToStartOf1970 * wil::HundredNanoSecondsInSecond };
__time64_t time_t1 = wil::DateTime_to_time_t(time1);
REQUIRE(time_t1 == 0);
// Verifying that converting back to DateTime would return the same value
DateTime time2 = wil::time_t_to_DateTime(time_t1);
REQUIRE(time1.UniversalTime == time2.UniversalTime);
// Verifying that converting to time_t for non-zero value also works
time2.UniversalTime += wil::HundredNanoSecondsInSecond * 123;
__time64_t time_t2 = wil::DateTime_to_time_t(time2);
REQUIRE(time_t2 - time_t1 == 123);
// Verifying that converting back to DateTime for non-zero value also works
time1 = wil::time_t_to_DateTime(time_t2);
REQUIRE(time1.UniversalTime == time2.UniversalTime);
}
ComPtr<IVector<IInspectable*>> MakeSampleInspectableVector()
{
auto result = Make<FakeVector<IInspectable*>>();
REQUIRE(result);
ComPtr<IPropertyValueStatics> propStatics;
REQUIRE_SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), &propStatics));
for (UINT32 i = 0; i < 5; ++i)
{
ComPtr<IInspectable> myProp;
REQUIRE_SUCCEEDED(propStatics->CreateUInt32(i, &myProp));
REQUIRE_SUCCEEDED(result->Append(myProp.Get()));
}
return result;
}
ComPtr<IVector<HSTRING>> MakeSampleStringVector()
{
auto result = Make<FakeVector<HSTRING>>();
REQUIRE(result);
const HStringReference items[] = { HStringReference(L"one"), HStringReference(L"two"), HStringReference(L"three") };
for (const auto& i : items)
{
REQUIRE_SUCCEEDED(result->Append(i.Get()));
}
return result;
}
ComPtr<IVector<Point>> MakeSamplePointVector()
{
auto result = Make<FakeVector<Point>>();
REQUIRE(result);
for (int i = 0; i < 5; ++i)
{
auto value = static_cast<float>(i);
REQUIRE_SUCCEEDED(result->Append(Point{ value, value }));
}
return result;
}
TEST_CASE("WinRTTests::VectorRangeTest", "[winrt][vector_range]")
{
auto uninit = wil::RoInitialize_failfast();
auto inspectables = MakeSampleInspectableVector();
unsigned count = 0;
REQUIRE_SUCCEEDED(inspectables->get_Size(&count));
unsigned idx = 0;
HRESULT success = S_OK;
for (const auto& i : wil::get_range_nothrow(inspectables.Get(), &success))
{
// Duplications are not a typo - they verify the thing is callable twice
UINT32 value;
ComPtr<IReference<UINT32>> intRef;
REQUIRE_SUCCEEDED(i.CopyTo(IID_PPV_ARGS(&intRef)));
REQUIRE_SUCCEEDED(intRef->get_Value(&value));
REQUIRE(idx == value);
REQUIRE_SUCCEEDED(i.CopyTo(IID_PPV_ARGS(&intRef)));
REQUIRE_SUCCEEDED(intRef->get_Value(&value));
REQUIRE(idx == value);
++idx;
HString rtc;
REQUIRE_SUCCEEDED(i->GetRuntimeClassName(rtc.GetAddressOf()));
REQUIRE_SUCCEEDED(i->GetRuntimeClassName(rtc.GetAddressOf()));
}
REQUIRE_SUCCEEDED(success);
REQUIRE(count == idx);
auto strings = MakeSampleStringVector();
for (const auto& i : wil::get_range_nothrow(strings.Get(), &success))
{
REQUIRE(i.Get());
REQUIRE(i.Get());
}
REQUIRE_SUCCEEDED(success);
int index = 0;
auto points = MakeSamplePointVector();
for (auto value : wil::get_range_nothrow(points.Get(), &success))
{
REQUIRE(index++ == value.Get().X);
}
REQUIRE_SUCCEEDED(success);
// operator-> should not clear out the pointer
auto inspRange = wil::get_range_nothrow(inspectables.Get());
for (auto itr = inspRange.begin(); itr != inspRange.end(); ++itr)
{
REQUIRE(itr->Get());
}
auto strRange = wil::get_range_nothrow(strings.Get());
for (auto itr = strRange.begin(); itr != strRange.end(); ++itr)
{
REQUIRE(itr->Get());
}
index = 0;
auto pointRange = wil::get_range_nothrow(points.Get());
for (auto itr = pointRange.begin(); itr != pointRange.end(); ++itr)
{
REQUIRE(index++ == itr->Get().X);
}
#if (defined WIL_ENABLE_EXCEPTIONS)
idx = 0;
for (const auto& i : wil::get_range(inspectables.Get()))
{
// Duplications are not a typo - they verify the thing is callable twice
UINT32 value;
ComPtr<IReference<UINT32>> intRef;
REQUIRE_SUCCEEDED(i.CopyTo(IID_PPV_ARGS(&intRef)));
REQUIRE_SUCCEEDED(intRef->get_Value(&value));
REQUIRE(idx == value);
REQUIRE_SUCCEEDED(i.CopyTo(IID_PPV_ARGS(&intRef)));
REQUIRE_SUCCEEDED(intRef->get_Value(&value));
REQUIRE(idx == value);
++idx;
HString rtc;
REQUIRE_SUCCEEDED(i->GetRuntimeClassName(rtc.GetAddressOf()));
REQUIRE_SUCCEEDED(i->GetRuntimeClassName(rtc.GetAddressOf()));
}
REQUIRE(count == idx);
for (const auto& i : wil::get_range(strings.Get()))
{
REQUIRE(i.Get());
REQUIRE(i.Get());
}
index = 0;
for (auto value : wil::get_range(points.Get()))
{
REQUIRE(index++ == value.Get().X);
}
// operator-> should not clear out the pointer
for (auto itr = inspRange.begin(); itr != inspRange.end(); ++itr)
{
REQUIRE(itr->Get());
}
for (auto itr = strRange.begin(); itr != strRange.end(); ++itr)
{
REQUIRE(itr->Get());
}
index = 0;
for (auto itr = pointRange.begin(); itr != pointRange.end(); ++itr)
{
REQUIRE(index++ == itr->Get().X);
}
// Iterator self-assignment is a nop.
{
auto inspRange2 = wil::get_range(inspectables.Get());
auto itr = inspRange2.begin();
REQUIRE(itr != inspRange2.end()); // should have something in it
auto& ref = *itr;
auto val = ref;
itr = itr;
REQUIRE(val == ref);
itr = std::move(itr);
REQUIRE(val == ref);
}
{
auto strRange2 = wil::get_range(strings.Get());
auto itr = strRange2.begin();
REQUIRE(itr != strRange2.end()); // should have something in it
auto& ref = *itr;
auto val = ref.Get();
itr = itr;
REQUIRE(val == ref);
itr = std::move(itr);
REQUIRE(val == ref.Get());
}
#endif
}
unsigned long GetComObjectRefCount(IUnknown* unk) { unk->AddRef(); return unk->Release(); }
TEST_CASE("WinRTTests::VectorRangeLeakTest", "[winrt][vector_range]")
{
auto uninit = wil::RoInitialize_failfast();
auto inspectables = MakeSampleInspectableVector();
ComPtr<IInspectable> verifyNotLeaked;
HRESULT hr = S_OK;
for (const auto& ptr : wil::get_range_nothrow(inspectables.Get(), &hr))
{
if (!verifyNotLeaked)
{
verifyNotLeaked = ptr;
}
}
inspectables = nullptr; // clear all refs to verifyNotLeaked
REQUIRE_SUCCEEDED(hr);
REQUIRE(GetComObjectRefCount(verifyNotLeaked.Get()) == 1);
inspectables = MakeSampleInspectableVector();
for (const auto& ptr : wil::get_range_failfast(inspectables.Get()))
{
if (!verifyNotLeaked)
{
verifyNotLeaked = ptr;
}
}
inspectables = nullptr; // clear all refs to verifyNotLeaked
REQUIRE(GetComObjectRefCount(verifyNotLeaked.Get()) == 1);
#if (defined WIL_ENABLE_EXCEPTIONS)
inspectables = MakeSampleInspectableVector();
for (const auto& ptr : wil::get_range(inspectables.Get()))
{
if (!verifyNotLeaked)
{
verifyNotLeaked = ptr;
}
}
inspectables = nullptr; // clear all refs to verifyNotLeaked
REQUIRE(GetComObjectRefCount(verifyNotLeaked.Get()) == 1);
#endif
}
TEST_CASE("WinRTTests::TwoPhaseConstructor", "[winrt][hstring]")
{
const wchar_t left[] = L"left";
const wchar_t right[] = L"right";
ULONG needed = ARRAYSIZE(left) + ARRAYSIZE(right) - 1;
auto maker = wil::TwoPhaseHStringConstructor::Preallocate(needed);
REQUIRE_SUCCEEDED(StringCbCopyW(maker.Get(), maker.ByteSize(), left));
REQUIRE_SUCCEEDED(StringCbCatW(maker.Get(), maker.ByteSize(), right));
REQUIRE_SUCCEEDED(maker.Validate(needed * sizeof(wchar_t)));
wil::unique_hstring promoted{ maker.Promote() };
REQUIRE(wcscmp(L"leftright", str_raw_ptr(promoted)) == 0);
}

209
Externals/WIL/tests/WistdTests.cpp vendored Normal file
View file

@ -0,0 +1,209 @@
#include <wil/wistd_functional.h>
#include "common.h"
#include "test_objects.h"
// Test methods/objects
int GetValue()
{
return 42;
}
int GetOtherValue()
{
return 8;
}
int Negate(int value)
{
return -value;
}
int Add(int lhs, int rhs)
{
return lhs + rhs;
}
TEST_CASE("WistdFunctionTests::CallOperatorTest", "[wistd]")
{
wistd::function<int()> getValue = GetValue;
REQUIRE(GetValue() == getValue());
wistd::function<int(int)> negate = Negate;
REQUIRE(Negate(42) == negate(42));
wistd::function<int(int, int)> add = Add;
REQUIRE(Add(42, 8) == add(42, 8));
}
TEST_CASE("WistdFunctionTests::AssignmentOperatorTest", "[wistd]")
{
wistd::function<int()> fn = GetValue;
REQUIRE(GetValue() == fn());
fn = GetOtherValue;
REQUIRE(GetOtherValue() == fn());
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("WistdFunctionTests::StdFunctionConstructionTest", "[wistd]")
{
// We should be able to capture a std::function in a wistd::function
wistd::function<int()> fn;
{
value_holder holder{ 42 };
std::function<int()> stdFn = [holder]()
{
return holder.value;
};
fn = stdFn;
}
REQUIRE(42 == fn());
}
#endif
TEST_CASE("WistdFunctionTests::CopyConstructionTest", "[wistd]")
{
object_counter_state state;
{
wistd::function<int()> copyFrom = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};
REQUIRE(0 == copyFrom());
auto copyTo = copyFrom;
REQUIRE(1 == copyTo());
}
REQUIRE(0 == state.instance_count());
}
TEST_CASE("WistdFunctionTests::CopyAssignmentTest", "[wistd]")
{
object_counter_state state;
{
wistd::function<int()> copyTo;
{
wistd::function<int()> copyFrom = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};
REQUIRE(0 == copyFrom());
copyTo = copyFrom;
}
REQUIRE(1 == copyTo());
}
REQUIRE(0 == state.instance_count());
}
TEST_CASE("WistdFunctionTests::MoveConstructionTest", "[wistd]")
{
object_counter_state state;
{
wistd::function<int()> moveFrom = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};
REQUIRE(0 == moveFrom());
auto moveTo = std::move(moveFrom);
REQUIRE(0 == moveTo());
// Because we move the underlying function object, we _must_ invalidate the moved from function
REQUIRE_FALSE(moveFrom != nullptr);
}
REQUIRE(0 == state.instance_count());
}
TEST_CASE("WistdFunctionTests::MoveAssignmentTest", "[wistd]")
{
object_counter_state state;
{
wistd::function<int()> moveTo;
{
wistd::function<int()> moveFrom = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};
REQUIRE(0 == moveFrom());
moveTo = std::move(moveFrom);
}
REQUIRE(0 == moveTo());
}
REQUIRE(0 == state.instance_count());
}
TEST_CASE("WistdFunctionTests::SwapTest", "[wistd]")
{
object_counter_state state;
{
wistd::function<int()> first;
wistd::function<int()> second;
first.swap(second);
REQUIRE_FALSE(first != nullptr);
REQUIRE_FALSE(second != nullptr);
first = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};
first.swap(second);
REQUIRE_FALSE(first != nullptr);
REQUIRE(second != nullptr);
REQUIRE(0 == second());
first.swap(second);
REQUIRE(first != nullptr);
REQUIRE_FALSE(second != nullptr);
REQUIRE(0 == first());
second = [counter = object_counter{ state }]()
{
return counter.state->copy_count;
};
first.swap(second);
REQUIRE(first != nullptr);
REQUIRE(second != nullptr);
REQUIRE(0 == first());
}
REQUIRE(0 == state.instance_count());
}
// MSVC's optimizer has had issues with wistd::function in the past when forwarding wistd::function objects to a
// function that accepts the arguments by value. This test exercises the workaround that we have in place. Note
// that this of course requires building with optimizations enabled
void ForwardingTest(wistd::function<int()> getValue, wistd::function<int(int)> negate, wistd::function<int(int, int)> add)
{
// Previously, this would cause a runtime crash
REQUIRE(Add(GetValue(), Negate(8)) == add(getValue(), negate(8)));
}
template <typename... Args>
void CallForwardingTest(Args&&... args)
{
ForwardingTest(wistd::forward<Args>(args)...);
}
TEST_CASE("WistdFunctionTests::OptimizationRegressionTest", "[wistd]")
{
CallForwardingTest(
wistd::function<int()>(GetValue),
wistd::function<int(int)>(Negate),
wistd::function<int(int, int)>(Add));
}

21
Externals/WIL/tests/app/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,21 @@
project(witest.app)
add_executable(witest.app)
add_definitions(-DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP)
target_sources(witest.app PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
)

14934
Externals/WIL/tests/catch.hpp vendored Normal file

File diff suppressed because it is too large Load diff

510
Externals/WIL/tests/common.h vendored Normal file
View file

@ -0,0 +1,510 @@
#pragma once
#include <windows.h>
#include <PathCch.h>
#include "catch.hpp"
#include <wil/filesystem.h>
#include <wil/result.h>
#define REPORTS_ERROR(expr) witest::ReportsError(wistd::is_same<HRESULT, decltype(expr)>{}, [&]() { return expr; })
#define REQUIRE_ERROR(expr) REQUIRE(REPORTS_ERROR(expr))
#define REQUIRE_NOERROR(expr) REQUIRE_FALSE(REPORTS_ERROR(expr))
#define CRASHES(expr) witest::DoesCodeCrash([&]() { return expr; })
#define REQUIRE_CRASH(expr) REQUIRE(CRASHES(expr))
#define REQUIRE_NOCRASH(expr) REQUIRE_FALSE(CRASHES(expr))
// NOTE: SUCCEEDED/FAILED macros not used here since Catch2 can give us better diagnostics if it knows the HRESULT value
#define REQUIRE_SUCCEEDED(expr) REQUIRE((HRESULT)(expr) >= 0)
#define REQUIRE_FAILED(expr) REQUIRE((HRESULT)(expr) < 0)
// MACRO double evaluation check.
// The following macro illustrates a common problem with writing macros:
// #define MY_MAX(a, b) (((a) > (b)) ? (a) : (b))
// The issue is that whatever code is being used for both a and b is being executed twice.
// This isn't harmful when thinking of constant numerics, but consider this example:
// MY_MAX(4, InterlockedIncrement(&cCount))
// This evaluates the (B) parameter twice and results in incrementing the counter twice.
// We use MDEC in unit tests to verify that this kind of pattern is not present. A test
// of this kind:
// MY_MAX(MDEC(4), MDEC(InterlockedIncrement(&cCount))
// will verify that the parameters are not evaluated more than once.
#define MDEC(PARAM) (witest::details::MacroDoubleEvaluationCheck(__LINE__, #PARAM), PARAM)
// There's some functionality that we need for testing that's not available for the app partition. Since those tests are
// primarily compilation tests, declare what's needed here
extern "C" {
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
WINBASEAPI _Ret_maybenull_
PVOID WINAPI AddVectoredExceptionHandler(_In_ ULONG First, _In_ PVECTORED_EXCEPTION_HANDLER Handler);
WINBASEAPI
ULONG WINAPI RemoveVectoredExceptionHandler(_In_ PVOID Handle);
#endif
}
#pragma warning(push)
#pragma warning(disable: 4702) // Unreachable code
namespace witest
{
namespace details
{
inline void MacroDoubleEvaluationCheck(size_t uLine, _In_ const char* pszCode)
{
struct SEval
{
size_t uLine;
const char* pszCode;
};
static SEval rgEval[15] = {};
static size_t nOffset = 0;
for (auto& eval : rgEval)
{
if ((eval.uLine == uLine) && (eval.pszCode != nullptr) && (0 == strcmp(pszCode, eval.pszCode)))
{
// This verification indicates that macro-double-evaluation check is firing for a particular usage of MDEC().
FAIL("Expression '" << pszCode << "' double evaluated in macro on line " << uLine);
}
}
rgEval[nOffset].uLine = uLine;
rgEval[nOffset].pszCode = pszCode;
nOffset = (nOffset + 1) % ARRAYSIZE(rgEval);
}
template <typename T>
class AssignTemporaryValueCleanup
{
public:
AssignTemporaryValueCleanup(_In_ AssignTemporaryValueCleanup const &) = delete;
AssignTemporaryValueCleanup & operator=(_In_ AssignTemporaryValueCleanup const &) = delete;
explicit AssignTemporaryValueCleanup(_Inout_ T *pVal, T val) WI_NOEXCEPT :
m_pVal(pVal),
m_valOld(*pVal)
{
*pVal = val;
}
AssignTemporaryValueCleanup(_Inout_ AssignTemporaryValueCleanup && other) WI_NOEXCEPT :
m_pVal(other.m_pVal),
m_valOld(other.m_valOld)
{
other.m_pVal = nullptr;
}
~AssignTemporaryValueCleanup() WI_NOEXCEPT
{
operator()();
}
void operator()() WI_NOEXCEPT
{
if (m_pVal != nullptr)
{
*m_pVal = m_valOld;
m_pVal = nullptr;
}
}
void Dismiss() WI_NOEXCEPT
{
m_pVal = nullptr;
}
private:
T *m_pVal;
T m_valOld;
};
}
// Use the following routine to allow for a variable to be swapped with another and automatically revert the
// assignment at the end of the scope.
// Example:
// int nFoo = 10
// {
// auto revert = witest::AssignTemporaryValue(&nFoo, 12);
// // nFoo will now be 12 within this scope...
// }
// // and nFoo is back to 10 within the outer scope
template <typename T>
inline witest::details::AssignTemporaryValueCleanup<T> AssignTemporaryValue(_Inout_ T *pVal, T val) WI_NOEXCEPT
{
return witest::details::AssignTemporaryValueCleanup<T>(pVal, val);
}
//! Global class which tracks objects that derive from @ref AllocatedObject.
//! Use `witest::g_objectCount.Leaked()` to determine if an object deriving from `AllocatedObject` has been leaked.
class GlobalCount
{
public:
int m_count = 0;
//! Returns `true` if there are any objects that derive from @ref AllocatedObject still in memory.
bool Leaked() const
{
return (m_count != 0);
}
~GlobalCount()
{
if (Leaked())
{
// NOTE: This runs when no test is active, but will still cause an assert failure to notify
FAIL("GlobalCount is non-zero; there is a leak somewhere");
}
}
};
__declspec(selectany) GlobalCount g_objectCount;
//! Derive an allocated test object from witest::AllocatedObject to ensure that those objects aren't leaked in the test.
//! Note that you can call g_objectCount.Leaked() at any point to determine if a leak has already occurred (assuming that
//! all objects should have been destroyed at that point.
class AllocatedObject
{
public:
AllocatedObject() { g_objectCount.m_count++; }
~AllocatedObject() { g_objectCount.m_count--; }
};
template <typename Lambda>
bool DoesCodeThrow(Lambda&& callOp)
{
#ifdef WIL_ENABLE_EXCEPTIONS
try
#endif
{
callOp();
}
#ifdef WIL_ENABLE_EXCEPTIONS
catch (...)
{
return true;
}
#endif
return false;
}
[[noreturn]]
inline void __stdcall TranslateFailFastException(PEXCEPTION_RECORD rec, PCONTEXT, DWORD)
{
// RaiseFailFastException cannot be continued or handled. By instead calling RaiseException, it allows us to
// handle exceptions
::RaiseException(rec->ExceptionCode, rec->ExceptionFlags, rec->NumberParameters, rec->ExceptionInformation);
#ifdef __clang__
__builtin_unreachable();
#endif
}
constexpr DWORD msvc_exception_code = 0xE06D7363;
// This is a MAJOR hack. Catch2 registers a vectored exception handler - which gets run before our handler below -
// that interprets a set of exception codes as fatal. We don't want this behavior since we may be expecting such
// crashes, so instead translate all exception codes to something not fatal
inline LONG WINAPI TranslateExceptionCodeHandler(PEXCEPTION_POINTERS info)
{
if (info->ExceptionRecord->ExceptionCode != witest::msvc_exception_code)
{
info->ExceptionRecord->ExceptionCode = STATUS_STACK_BUFFER_OVERRUN;
}
return EXCEPTION_CONTINUE_SEARCH;
}
namespace details
{
inline bool DoesCodeCrash(wistd::function<void()>& callOp)
{
bool result = false;
__try
{
callOp();
}
// Let C++ exceptions pass through
__except ((::GetExceptionCode() != msvc_exception_code) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
result = true;
}
return result;
}
}
inline bool DoesCodeCrash(wistd::function<void()> callOp)
{
// See above; we don't want to actually fail fast, so make sure we raise a different exception instead
auto restoreHandler = AssignTemporaryValue(&wil::details::g_pfnRaiseFailFastException, TranslateFailFastException);
auto handler = AddVectoredExceptionHandler(1, TranslateExceptionCodeHandler);
auto removeVectoredHandler = wil::scope_exit([&] { RemoveVectoredExceptionHandler(handler); });
return details::DoesCodeCrash(callOp);
}
template <typename Lambda>
bool ReportsError(wistd::false_type, Lambda&& callOp)
{
bool doesThrow = false;
bool doesCrash = DoesCodeCrash([&]()
{
doesThrow = DoesCodeThrow(callOp);
});
return doesThrow || doesCrash;
}
template <typename Lambda>
bool ReportsError(wistd::true_type, Lambda&& callOp)
{
return FAILED(callOp());
}
#ifdef WIL_ENABLE_EXCEPTIONS
class TestFailureCache final :
public wil::details::IFailureCallback
{
public:
TestFailureCache() :
m_callbackHolder(this)
{
}
void clear()
{
m_failures.clear();
}
size_t size() const
{
return m_failures.size();
}
bool empty() const
{
return m_failures.empty();
}
const wil::FailureInfo& operator[](size_t pos) const
{
return m_failures.at(pos).GetFailureInfo();
}
// IFailureCallback
bool NotifyFailure(wil::FailureInfo const & failure) WI_NOEXCEPT override
{
m_failures.emplace_back(failure);
return false;
}
private:
std::vector<wil::StoredFailureInfo> m_failures;
wil::details::ThreadFailureCallbackHolder m_callbackHolder;
};
#endif
inline HRESULT GetTempFileName(wchar_t (&result)[MAX_PATH])
{
wchar_t dir[MAX_PATH];
RETURN_LAST_ERROR_IF(::GetTempPathW(MAX_PATH, dir) == 0);
RETURN_LAST_ERROR_IF(::GetTempFileNameW(dir, L"wil", 0, result) == 0);
return S_OK;
}
inline HRESULT CreateUniqueFolderPath(wchar_t (&buffer)[MAX_PATH], PCWSTR root = nullptr)
{
if (root)
{
RETURN_LAST_ERROR_IF(::GetTempFileNameW(root, L"wil", 0, buffer) == 0);
}
else
{
wchar_t tempPath[MAX_PATH];
RETURN_LAST_ERROR_IF(::GetTempPathW(ARRAYSIZE(tempPath), tempPath) == 0);
RETURN_LAST_ERROR_IF(::GetLongPathNameW(tempPath, tempPath, ARRAYSIZE(tempPath)) == 0);
RETURN_LAST_ERROR_IF(::GetTempFileNameW(tempPath, L"wil", 0, buffer) == 0);
}
RETURN_IF_WIN32_BOOL_FALSE(DeleteFileW(buffer));
PathCchRemoveExtension(buffer, ARRAYSIZE(buffer));
return S_OK;
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
struct TestFolder
{
TestFolder()
{
if (SUCCEEDED(CreateUniqueFolderPath(m_path)) && SUCCEEDED(wil::CreateDirectoryDeepNoThrow(m_path)))
{
m_valid = true;
}
}
TestFolder(PCWSTR path)
{
if (SUCCEEDED(StringCchCopyW(m_path, ARRAYSIZE(m_path), path)) && SUCCEEDED(wil::CreateDirectoryDeepNoThrow(m_path)))
{
m_valid = true;
}
}
TestFolder(const TestFolder&) = delete;
TestFolder& operator=(const TestFolder&) = delete;
TestFolder(TestFolder&& other)
{
if (other.m_valid)
{
m_valid = true;
other.m_valid = false;
wcscpy_s(m_path, other.m_path);
}
}
~TestFolder()
{
if (m_valid)
{
wil::RemoveDirectoryRecursiveNoThrow(m_path);
}
}
operator bool() const
{
return m_valid;
}
operator PCWSTR() const
{
return m_path;
}
PCWSTR Path() const
{
return m_path;
}
private:
bool m_valid = false;
wchar_t m_path[MAX_PATH] = L"";
};
struct TestFile
{
TestFile(PCWSTR path)
{
if (SUCCEEDED(StringCchCopyW(m_path, ARRAYSIZE(m_path), path)))
{
Create();
}
}
TestFile(PCWSTR dirPath, PCWSTR fileName)
{
if (SUCCEEDED(StringCchCopyW(m_path, ARRAYSIZE(m_path), dirPath)) && SUCCEEDED(PathCchAppend(m_path, ARRAYSIZE(m_path), fileName)))
{
Create();
}
}
TestFile(const TestFile&) = delete;
TestFile& operator=(const TestFile&) = delete;
TestFile(TestFile&& other)
{
if (other.m_valid)
{
m_valid = true;
m_deleteDir = other.m_deleteDir;
other.m_valid = other.m_deleteDir = false;
wcscpy_s(m_path, other.m_path);
}
}
~TestFile()
{
// Best effort on all of these
if (m_valid)
{
::DeleteFileW(m_path);
}
if (m_deleteDir)
{
size_t parentLength;
if (wil::try_get_parent_path_range(m_path, &parentLength))
{
m_path[parentLength] = L'\0';
::RemoveDirectoryW(m_path);
m_path[parentLength] = L'\\';
}
}
}
operator bool() const
{
return m_valid;
}
operator PCWSTR() const
{
return m_path;
}
PCWSTR Path() const
{
return m_path;
}
private:
HRESULT Create()
{
WI_ASSERT(!m_valid && !m_deleteDir);
wil::unique_hfile fileHandle(::CreateFileW(m_path,
FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
if (!fileHandle)
{
auto err = ::GetLastError();
size_t parentLength;
if ((err == ERROR_PATH_NOT_FOUND) && wil::try_get_parent_path_range(m_path, &parentLength))
{
m_path[parentLength] = L'\0';
RETURN_IF_FAILED(wil::CreateDirectoryDeepNoThrow(m_path));
m_deleteDir = true;
m_path[parentLength] = L'\\';
fileHandle.reset(::CreateFileW(m_path,
FILE_WRITE_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
RETURN_LAST_ERROR_IF(!fileHandle);
}
else
{
RETURN_WIN32(err);
}
}
m_valid = true;
return S_OK;
}
bool m_valid = false;
bool m_deleteDir = false;
wchar_t m_path[MAX_PATH] = L"";
};
#endif
}
#pragma warning(pop)

View file

@ -0,0 +1,31 @@
# Compilers often don't use the latest C++ standard as the default. Periodically update this value (possibly conditioned
# on compiler) as new standards are ratified/support is available
set(CMAKE_CXX_STANDARD 17)
project(witest.cpplatest)
add_executable(witest.cpplatest)
# Semi-arbitrary insiders SDK version selected that uses C++/WinRT "2.0"
if ("${WIL_WINDOWS_SDK_VERSION}" VERSION_GREATER_EQUAL "10.0.18878.0")
target_sources(witest.cpplatest PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRT20Tests.cpp)
endif()
target_sources(witest.cpplatest PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
)

8
Externals/WIL/tests/main.cpp vendored Normal file
View file

@ -0,0 +1,8 @@
#pragma comment(lib, "Pathcch.lib")
#pragma comment(lib, "RuntimeObject.lib")
#pragma comment(lib, "Synchronization.lib")
#pragma comment(lib, "RpcRt4.lib")
#define CATCH_CONFIG_MAIN
#include "catch.hpp"

View file

@ -0,0 +1,27 @@
project(witest.noexcept)
add_executable(witest.noexcept)
# Turn off exceptions for this test
replace_cxx_flag("/EHsc" "")
add_definitions(-DCATCH_CONFIG_DISABLE_EXCEPTIONS)
# Catch2 has a no exceptions mode (configured above), however still includes STL headers which contain try...catch
# statements... Thankfully MSVC just gives us a warning that we can disable
append_cxx_flag("/wd4530")
target_sources(witest.noexcept PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp)

View file

@ -0,0 +1,21 @@
project(witest)
add_executable(witest)
target_sources(witest PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
)

108
Externals/WIL/tests/test_objects.h vendored Normal file
View file

@ -0,0 +1,108 @@
#pragma once
#include "catch.hpp"
// Useful for validating that the copy constructor is never called (e.g. to validate perfect forwarding). Note that
// the copy constructor/assignment operator are not deleted since we want to be able to validate in scenarios that
// require CopyConstructible (e.g. for wistd::function)
struct fail_on_copy
{
fail_on_copy() = default;
fail_on_copy(const fail_on_copy&)
{
FAIL("Copy constructor invoked for fail_on_copy type");
}
fail_on_copy(fail_on_copy&&) = default;
fail_on_copy& operator=(const fail_on_copy&)
{
FAIL("Copy assignment operator invoked for fail_on_copy type");
return *this;
}
fail_on_copy& operator=(fail_on_copy&&) = default;
};
// Useful for validating that objects get copied e.g. as opposed to capturing a reference
struct value_holder
{
int value = 0xbadf00d;
~value_holder()
{
value = 0xbadf00d;
}
};
// Useful for validating that functions, etc. are callable with move-only types
// Example real type that is move only is Microsoft::WRL::Wrappers::HString
struct cannot_copy
{
cannot_copy() = default;
cannot_copy(const cannot_copy&) = delete;
cannot_copy& operator=(const cannot_copy&) = delete;
cannot_copy(cannot_copy&&) = default;
cannot_copy& operator=(cannot_copy&&) = default;
};
// State for object_counter type. This has the unfortunate side effect that the object_counter type cannot be used in
// contexts that require a default constructible type, but has the nice property that it allows for tests to run
// concurrently
struct object_counter_state
{
volatile LONG constructed_count = 0;
volatile LONG destructed_count = 0;
volatile LONG copy_count = 0;
volatile LONG move_count = 0;
LONG instance_count()
{
return constructed_count - destructed_count;
}
};
struct object_counter
{
object_counter_state* state;
object_counter(object_counter_state& s) :
state(&s)
{
::InterlockedIncrement(&state->constructed_count);
}
object_counter(const object_counter& other) :
state(other.state)
{
::InterlockedIncrement(&state->constructed_count);
::InterlockedIncrement(&state->copy_count);
}
object_counter(object_counter&& other) WI_NOEXCEPT :
state(other.state)
{
::InterlockedIncrement(&state->constructed_count);
::InterlockedIncrement(&state->move_count);
}
~object_counter()
{
::InterlockedIncrement(&state->destructed_count);
state = nullptr;
}
object_counter& operator=(const object_counter&)
{
::InterlockedIncrement(&state->copy_count);
return *this;
}
object_counter& operator=(object_counter&&) WI_NOEXCEPT
{
::InterlockedIncrement(&state->move_count);
return *this;
}
};

3472
Externals/WIL/tests/wiTest.cpp vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,2 @@
We try and be as conformant as possible, but sometimes dependencies make that difficult. For example, WRL has had a number of conformance issues that keep getting uncovered. The files here are fixed up copies of those files and the include path is modified such that these directories appear first.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -60,6 +60,8 @@ Dolphin includes or links code of the following third-party software projects:
[LGPLv2+](http://www.surina.net/soundtouch/license.html)
- [TAP-Windows](https://openvpn.net/):
header only
- [Windows Implementation Libraries](https://github.com/microsoft/wil):
[MIT](https://github.com/microsoft/wil/blob/master/LICENSE)
- [xxHash](https://github.com/Cyan4973/xxHash):
[2-clause BSD](https://github.com/Cyan4973/xxHash/blob/master/LICENSE)
- [zlib](http://www.zlib.net/):

View file

@ -53,6 +53,7 @@
<AdditionalIncludeDirectories>$(ExternalsDir)pugixml;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ExternalsDir)SFML\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ExternalsDir)Vulkan\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ExternalsDir)WIL\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ExternalsDir)xxhash;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ExternalsDir)zlib;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>FMT_HEADER_ONLY=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
@ -62,6 +63,7 @@
<PreprocessorDefinitions>USE_ANALYTICS=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>USE_DISCORD_PRESENCE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>CURL_STATICLIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>WIL_SUPPRESS_EXCEPTIONS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'=='x64'">_ARCH_64=1;_M_X86=1;_M_X86_64=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'=='ARM64'">_ARCH_64=1;_M_ARM_64=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Platform)'=='x64'">HAVE_FFMPEG;%(PreprocessorDefinitions)</PreprocessorDefinitions>