mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
Merge pull request #14173 from JoshuaVandaele/WIL-submodule
Make WIL a submodule and update it
This commit is contained in:
@@ -130,3 +130,7 @@
|
||||
path = Externals/imgui/imgui
|
||||
url = https://github.com/ocornut/imgui.git
|
||||
shallow = true
|
||||
[submodule "Externals/wil"]
|
||||
path = Externals/wil
|
||||
url = https://github.com/microsoft/wil.git
|
||||
shallow = true
|
||||
|
||||
+1
-1
@@ -755,7 +755,7 @@ else()
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
include_directories(Externals/WIL/include)
|
||||
include_directories(Externals/wil/include)
|
||||
include_directories(Externals/OpenAL/include)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
# Disable CRLF-mapping for all files in the depot.
|
||||
* -text
|
||||
@@ -1,336 +0,0 @@
|
||||
## 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/
|
||||
Vendored
-63
@@ -1,63 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(WIL)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# 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)
|
||||
option(WIL_BUILD_PACKAGING "Sets option to build the packaging, default on" ON)
|
||||
option(WIL_BUILD_TESTS "Sets option to build the unit tests, default on" ON)
|
||||
|
||||
if (NOT DEFINED WIL_BUILD_VERSION)
|
||||
set(WIL_BUILD_VERSION "0.0.0")
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED CPPWINRT_VERSION)
|
||||
set(CPPWINRT_VERSION "2.0.221121.5")
|
||||
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()
|
||||
|
||||
if (${WIL_BUILD_PACKAGING})
|
||||
add_subdirectory(packaging)
|
||||
endif()
|
||||
|
||||
if (${WIL_BUILD_TESTS})
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
# Gather headers into an interface library.
|
||||
file(GLOB_RECURSE HEADER_FILES "${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/*.h")
|
||||
add_library(${PROJECT_NAME} INTERFACE)
|
||||
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
||||
|
||||
# The interface's include directory.
|
||||
target_include_directories(${PROJECT_NAME} INTERFACE
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
)
|
||||
|
||||
# Include the .natvis files
|
||||
if (MSVC)
|
||||
target_sources(${PROJECT_NAME} INTERFACE
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/natvis/wil.natvis>")
|
||||
endif()
|
||||
|
||||
# Install Package Configuration
|
||||
string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER)
|
||||
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME_LOWER}_targets)
|
||||
install(EXPORT ${PROJECT_NAME_LOWER}_targets
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
FILE ${PROJECT_NAME_LOWER}Config.cmake
|
||||
DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME}"
|
||||
)
|
||||
|
||||
# Install the headers at a standard cmake location.
|
||||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/wil" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
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.
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
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
|
||||
Vendored
-99
@@ -1,99 +0,0 @@
|
||||
# Windows Implementation Libraries (WIL)
|
||||
|
||||
[](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)
|
||||
([documentation](https://github.com/microsoft/wil/wiki/Win32-helpers)): 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.
|
||||
- [`include/wil/Tracelogging.h`](include/wil/Tracelogging.h): This file contains the convenience macros
|
||||
that enable developers define and log telemetry. These macros use
|
||||
[`TraceLogging API`](https://docs.microsoft.com/en-us/windows/win32/tracelogging/trace-logging-portal)
|
||||
to log data. This data can be viewed in tools such as
|
||||
[`Windows Performance Analyzer`](https://docs.microsoft.com/en-us/windows-hardware/test/wpt/windows-performance-analyzer).
|
||||
|
||||
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 2022"). 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 2022")
|
||||
|
||||
# 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.
|
||||
Vendored
-60
@@ -1,60 +0,0 @@
|
||||
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.
|
||||
-71
@@ -1,71 +0,0 @@
|
||||
|
||||
# This is unfortunately still needed to disable exceptions/RTTI since modern CMake still has no builtin support...
|
||||
# E.g. replace_cxx_flag("/EHsc", "/EHs-c-")
|
||||
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()
|
||||
|
||||
# Fixup default compiler settings
|
||||
add_compile_options(
|
||||
# Be as strict as reasonably possible, since we want to support consumers using strict warning levels
|
||||
/W4 /WX
|
||||
)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options(
|
||||
# Ignore some pedantic warnings enabled by '-Wextra'
|
||||
-Wno-missing-field-initializers
|
||||
|
||||
# Ignore some pedantic warnings enabled by '-Wpedantic'
|
||||
-Wno-language-extension-token
|
||||
-Wno-c++17-attribute-extensions
|
||||
-Wno-gnu-zero-variadic-macro-arguments
|
||||
-Wno-extra-semi
|
||||
|
||||
# For tests, we want to be able to test self assignment, so disable this warning
|
||||
-Wno-self-assign-overloaded
|
||||
-Wno-self-move
|
||||
|
||||
# clang needs this to enable _InterlockedCompareExchange128
|
||||
-mcx16
|
||||
|
||||
# We don't want legacy MSVC conformance
|
||||
-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
|
||||
# -fno-ms-compatibility
|
||||
# -ferror-limit=999
|
||||
# -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)
|
||||
# -Xclang -std=c++2a
|
||||
)
|
||||
else()
|
||||
add_compile_options(
|
||||
# We want to be as conformant as possible, so tell MSVC to not be permissive (note that this has no effect on clang-cl)
|
||||
/permissive-
|
||||
|
||||
# wistd::function has padding due to alignment. This is expected
|
||||
/wd4324
|
||||
|
||||
# TODO: https://github.com/Microsoft/wil/issues/6
|
||||
# /experimental:preprocessor
|
||||
|
||||
# CRT headers are not yet /experimental:preprocessor clean, so work around the known issues
|
||||
# /Wv:18
|
||||
|
||||
# Some tests have a LOT of template instantiations
|
||||
/bigobj
|
||||
|
||||
# NOTE: Temporary workaround while https://github.com/microsoft/wil/issues/102 is being investigated
|
||||
/d2FH4-
|
||||
)
|
||||
endif()
|
||||
-3359
File diff suppressed because it is too large
Load Diff
Vendored
-3007
File diff suppressed because it is too large
Load Diff
-467
@@ -1,467 +0,0 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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_COM_APARTMENT_VARIABLE_INCLUDED
|
||||
#define __WIL_COM_APARTMENT_VARIABLE_INCLUDED
|
||||
|
||||
#include <any>
|
||||
#include <objidl.h>
|
||||
#include <roapi.h>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
|
||||
#include "com.h"
|
||||
#include "cppwinrt.h"
|
||||
#include "result_macros.h"
|
||||
#include "win32_helpers.h"
|
||||
|
||||
#ifndef WIL_ENABLE_EXCEPTIONS
|
||||
#error This header requires exceptions
|
||||
#endif
|
||||
|
||||
namespace wil
|
||||
{
|
||||
// Determine if apartment variables are supported in the current process context.
|
||||
// Prior to build 22365, the APIs needed to create apartment variables (e.g. RoGetApartmentIdentifier)
|
||||
// failed for unpackaged processes. For MS people, see http://task.ms/31861017 for details.
|
||||
// APIs needed to implement apartment variables did not work in non-packaged processes.
|
||||
inline bool are_apartment_variables_supported()
|
||||
{
|
||||
unsigned long long apartmentId{};
|
||||
return RoGetApartmentIdentifier(&apartmentId) != HRESULT_FROM_WIN32(ERROR_API_UNAVAILABLE);
|
||||
}
|
||||
|
||||
// COM will implicitly rundown the apartment registration when it invokes a handler
|
||||
// and blocks calling unregister when executing the callback. So be careful to release()
|
||||
// this when callback is invoked to avoid a double free of the cookie.
|
||||
using unique_apartment_shutdown_registration = unique_any<APARTMENT_SHUTDOWN_REGISTRATION_COOKIE, decltype(&::RoUnregisterForApartmentShutdown), ::RoUnregisterForApartmentShutdown>;
|
||||
|
||||
struct apartment_variable_platform
|
||||
{
|
||||
static unsigned long long GetApartmentId()
|
||||
{
|
||||
unsigned long long apartmentId{};
|
||||
FAIL_FAST_IF_FAILED(RoGetApartmentIdentifier(&apartmentId));
|
||||
return apartmentId;
|
||||
}
|
||||
|
||||
static auto RegisterForApartmentShutdown(IApartmentShutdown* observer)
|
||||
{
|
||||
unsigned long long id{};
|
||||
shutdown_type cookie;
|
||||
THROW_IF_FAILED(RoRegisterForApartmentShutdown(observer, &id, cookie.put()));
|
||||
return cookie;
|
||||
}
|
||||
|
||||
static void UnRegisterForApartmentShutdown(APARTMENT_SHUTDOWN_REGISTRATION_COOKIE cookie)
|
||||
{
|
||||
FAIL_FAST_IF_FAILED(RoUnregisterForApartmentShutdown(cookie));
|
||||
}
|
||||
|
||||
static auto CoInitializeEx(DWORD coinitFlags = 0 /*COINIT_MULTITHREADED*/)
|
||||
{
|
||||
return wil::CoInitializeEx(coinitFlags);
|
||||
}
|
||||
|
||||
// disable the test hook
|
||||
inline static constexpr unsigned long AsyncRundownDelayForTestingRaces = INFINITE;
|
||||
|
||||
using shutdown_type = wil::unique_apartment_shutdown_registration;
|
||||
};
|
||||
|
||||
enum class apartment_variable_leak_action { fail_fast, ignore };
|
||||
|
||||
// "pins" the current module in memory by incrementing the module reference count and leaking that.
|
||||
inline void ensure_module_stays_loaded()
|
||||
{
|
||||
static INIT_ONCE s_initLeakModule{}; // avoiding magic statics
|
||||
wil::init_once_failfast(s_initLeakModule, []()
|
||||
{
|
||||
HMODULE result{};
|
||||
FAIL_FAST_IF(!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, L"", &result));
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
namespace details
|
||||
{
|
||||
// For the address of data, you can detect global variables by the ability to resolve the module from the address.
|
||||
inline bool IsGlobalVariable(const void* moduleAddress) noexcept
|
||||
{
|
||||
wil::unique_hmodule moduleHandle;
|
||||
return GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<PCWSTR>(moduleAddress), &moduleHandle) != FALSE;
|
||||
}
|
||||
|
||||
struct any_maker_base
|
||||
{
|
||||
std::any(*adapter)(void*);
|
||||
void* inner;
|
||||
|
||||
std::any operator()() const
|
||||
{
|
||||
return adapter(inner);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct any_maker : any_maker_base
|
||||
{
|
||||
any_maker()
|
||||
{
|
||||
adapter = [](auto) -> std::any { return T{}; };
|
||||
}
|
||||
|
||||
any_maker(T(*maker)())
|
||||
{
|
||||
adapter = [](auto maker) -> std::any { return reinterpret_cast<T(*)()>(maker)(); };
|
||||
inner = reinterpret_cast<void*>(maker);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
any_maker(F&& f)
|
||||
{
|
||||
adapter = [](auto maker) -> std::any { return reinterpret_cast<F*>(maker)[0](); };
|
||||
inner = std::addressof(f);
|
||||
}
|
||||
};
|
||||
|
||||
template<apartment_variable_leak_action leak_action = apartment_variable_leak_action::fail_fast,
|
||||
typename test_hook = apartment_variable_platform>
|
||||
struct apartment_variable_base
|
||||
{
|
||||
inline static winrt::slim_mutex s_lock;
|
||||
|
||||
struct apartment_variable_storage
|
||||
{
|
||||
apartment_variable_storage(apartment_variable_storage&& other) noexcept = default;
|
||||
apartment_variable_storage(const apartment_variable_storage& other) = delete;
|
||||
|
||||
apartment_variable_storage(typename test_hook::shutdown_type&& cookie_) : cookie(std::move(cookie_))
|
||||
{
|
||||
}
|
||||
|
||||
winrt::apartment_context context;
|
||||
typename test_hook::shutdown_type cookie;
|
||||
// Variables are stored using the address of the apartment_variable_base<> as the key.
|
||||
std::unordered_map<apartment_variable_base<leak_action, test_hook>*, std::any> variables;
|
||||
};
|
||||
|
||||
// Apartment id -> variables storage.
|
||||
inline static wil::object_without_destructor_on_shutdown<
|
||||
std::unordered_map<unsigned long long, apartment_variable_storage>>
|
||||
s_apartmentStorage;
|
||||
|
||||
constexpr apartment_variable_base() = default;
|
||||
~apartment_variable_base()
|
||||
{
|
||||
// Global variables (object with static storage duration)
|
||||
// are run down when the process is shutting down or when the
|
||||
// dll is unloaded. At these points it is not possible to start
|
||||
// an async operation and the work performed is not needed,
|
||||
// the apartments with variable have been run down already.
|
||||
const auto isGlobal = details::IsGlobalVariable(this);
|
||||
if (!isGlobal)
|
||||
{
|
||||
clear_all_apartments_async();
|
||||
}
|
||||
|
||||
if constexpr (leak_action == apartment_variable_leak_action::fail_fast)
|
||||
{
|
||||
if (isGlobal && !ProcessShutdownInProgress())
|
||||
{
|
||||
// If you hit this fail fast it means the storage in s_apartmentStorage will be leaked.
|
||||
// For apartment variables used in .exes, this is expected and
|
||||
// this fail fast should be disabled using
|
||||
// wil::apartment_variable<T, wil::apartment_variable_leak_action::ignore>
|
||||
//
|
||||
// For DLLs, if this is expected, disable this fail fast using
|
||||
// wil::apartment_variable<T, wil::apartment_variable_leak_action::ignore>
|
||||
//
|
||||
// Use of apartment variables in DLLs only loaded by COM will never hit this case
|
||||
// as COM will unload DLLs before apartments are rundown,
|
||||
// providing the opportunity to empty s_apartmentStorage.
|
||||
//
|
||||
// But DLLs loaded and unloaded to call DLL entry points (outside of COM) may
|
||||
// create variable storage that can't be cleaned up as the DLL lifetime is
|
||||
// shorter that the COM lifetime. In these cases either
|
||||
// 1) accept the leaks and disable the fail fast as describe above
|
||||
// 2) disable module unloading by calling wil::ensure_module_stays_loaded
|
||||
// 3) CoCreate an object from this DLL to make COM aware of the DLL
|
||||
FAIL_FAST_IF(!s_apartmentStorage.get().empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// non-copyable, non-assignable
|
||||
apartment_variable_base(apartment_variable_base const&) = delete;
|
||||
void operator=(apartment_variable_base const&) = delete;
|
||||
|
||||
// get current value or throw if no value has been set
|
||||
std::any& get_existing()
|
||||
{
|
||||
if (auto any = get_if())
|
||||
{
|
||||
return *any;
|
||||
}
|
||||
THROW_HR(E_NOT_SET);
|
||||
}
|
||||
|
||||
static apartment_variable_storage* get_current_apartment_variable_storage()
|
||||
{
|
||||
auto storage = s_apartmentStorage.get().find(test_hook::GetApartmentId());
|
||||
if (storage != s_apartmentStorage.get().end())
|
||||
{
|
||||
return &storage->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
apartment_variable_storage* ensure_current_apartment_variables()
|
||||
{
|
||||
auto variables = get_current_apartment_variable_storage();
|
||||
if (variables)
|
||||
{
|
||||
return variables;
|
||||
}
|
||||
|
||||
struct ApartmentObserver : public winrt::implements<ApartmentObserver, IApartmentShutdown>
|
||||
{
|
||||
void STDMETHODCALLTYPE OnUninitialize(unsigned long long apartmentId) noexcept override
|
||||
{
|
||||
// This code runs at apartment rundown so be careful to avoid deadlocks by
|
||||
// extracting the variables under the lock then release them outside.
|
||||
auto variables = [apartmentId]()
|
||||
{
|
||||
auto lock = winrt::slim_lock_guard(s_lock);
|
||||
return s_apartmentStorage.get().extract(apartmentId);
|
||||
}();
|
||||
WI_ASSERT(variables.key() == apartmentId);
|
||||
// The system implicitly releases the shutdown observer
|
||||
// after invoking the callback and does not allow calling unregister
|
||||
// in the callback. So release the reference to the registration.
|
||||
variables.mapped().cookie.release();
|
||||
}
|
||||
};
|
||||
auto shutdownRegistration = test_hook::RegisterForApartmentShutdown(winrt::make<ApartmentObserver>().get());
|
||||
return &s_apartmentStorage.get().insert({ test_hook::GetApartmentId(), apartment_variable_storage(std::move(shutdownRegistration)) }).first->second;
|
||||
}
|
||||
|
||||
// get current value or custom-construct one on demand
|
||||
template<typename T>
|
||||
std::any& get_or_create(any_maker<T> && creator)
|
||||
{
|
||||
apartment_variable_storage* variable_storage = nullptr;
|
||||
|
||||
{ // scope for lock
|
||||
auto lock = winrt::slim_lock_guard(s_lock);
|
||||
variable_storage = ensure_current_apartment_variables();
|
||||
|
||||
auto variable = variable_storage->variables.find(this);
|
||||
if (variable != variable_storage->variables.end())
|
||||
{
|
||||
return variable->second;
|
||||
}
|
||||
} // drop the lock
|
||||
|
||||
// create the object outside the lock to avoid reentrant deadlock
|
||||
auto value = creator();
|
||||
|
||||
auto insert_lock = winrt::slim_lock_guard(s_lock);
|
||||
// The insertion may fail if creator() recursively caused itself to be created,
|
||||
// in which case we return the existing object and the falsely-created one is discarded.
|
||||
return variable_storage->variables.insert({ this, std::move(value) }).first->second;
|
||||
}
|
||||
|
||||
// get pointer to current value or nullptr if no value has been set
|
||||
std::any* get_if()
|
||||
{
|
||||
auto lock = winrt::slim_lock_guard(s_lock);
|
||||
|
||||
if (auto variable_storage = get_current_apartment_variable_storage())
|
||||
{
|
||||
auto variable = variable_storage->variables.find(this);
|
||||
if (variable != variable_storage->variables.end())
|
||||
{
|
||||
return &(variable->second);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// replace or create the current value, fail fasts if the value is not already stored
|
||||
void set(std::any value)
|
||||
{
|
||||
// release value, with the swapped value, outside of the lock
|
||||
{
|
||||
auto lock = winrt::slim_lock_guard(s_lock);
|
||||
auto storage = s_apartmentStorage.get().find(test_hook::GetApartmentId());
|
||||
FAIL_FAST_IF(storage == s_apartmentStorage.get().end());
|
||||
auto& variable_storage = storage->second;
|
||||
auto variable = variable_storage.variables.find(this);
|
||||
FAIL_FAST_IF(variable == variable_storage.variables.end());
|
||||
variable->second.swap(value);
|
||||
}
|
||||
}
|
||||
|
||||
// remove any current value
|
||||
void clear()
|
||||
{
|
||||
auto lock = winrt::slim_lock_guard(s_lock);
|
||||
if (auto variable_storage = get_current_apartment_variable_storage())
|
||||
{
|
||||
variable_storage->variables.erase(this);
|
||||
if (variable_storage->variables.size() == 0)
|
||||
{
|
||||
s_apartmentStorage.get().erase(test_hook::GetApartmentId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
winrt::Windows::Foundation::IAsyncAction clear_all_apartments_async()
|
||||
{
|
||||
// gather all the apartments that hold objects we need to destruct
|
||||
// (do not gather the objects themselves, because the apartment might
|
||||
// destruct before we get around to it, and we should let the apartment
|
||||
// destruct the object while it still can).
|
||||
|
||||
std::vector<winrt::apartment_context> contexts;
|
||||
{ // scope for lock
|
||||
auto lock = winrt::slim_lock_guard(s_lock);
|
||||
for (auto& [id, storage] : s_apartmentStorage.get())
|
||||
{
|
||||
auto variable = storage.variables.find(this);
|
||||
if (variable != storage.variables.end())
|
||||
{
|
||||
contexts.push_back(storage.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (contexts.empty())
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
|
||||
wil::unique_mta_usage_cookie mta_reference; // need to extend the MTA due to async cleanup
|
||||
FAIL_FAST_IF_FAILED(CoIncrementMTAUsage(mta_reference.put()));
|
||||
|
||||
// From a background thread hop into each apartment to run down the object
|
||||
// if it's still there.
|
||||
co_await winrt::resume_background();
|
||||
|
||||
// This hook enables testing the case where execution of this method loses the race with
|
||||
// apartment rundown by other means.
|
||||
if constexpr (test_hook::AsyncRundownDelayForTestingRaces != INFINITE)
|
||||
{
|
||||
Sleep(test_hook::AsyncRundownDelayForTestingRaces);
|
||||
}
|
||||
|
||||
for (auto&& context : contexts)
|
||||
{
|
||||
try
|
||||
{
|
||||
co_await context;
|
||||
clear();
|
||||
}
|
||||
catch (winrt::hresult_error const& e)
|
||||
{
|
||||
// Ignore failure if apartment ran down before we could clean it up.
|
||||
// The object already ran down as part of apartment cleanup.
|
||||
if ((e.code() != RPC_E_SERVER_DIED_DNE) &&
|
||||
(e.code() != RPC_E_DISCONNECTED))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
FAIL_FAST();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const auto& storage()
|
||||
{
|
||||
return s_apartmentStorage.get();
|
||||
}
|
||||
|
||||
static size_t current_apartment_variable_count()
|
||||
{
|
||||
auto lock = winrt::slim_lock_guard(s_lock);
|
||||
if (auto variable_storage = get_current_apartment_variable_storage())
|
||||
{
|
||||
return variable_storage->variables.size();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Apartment variables enable storing COM objects safely in globals
|
||||
// (objects with static storage duration) by creating a unique copy
|
||||
// in each apartment and managing their lifetime based on apartment rundown
|
||||
// notifications.
|
||||
// They can also be used for automatic or dynamic storage duration but those
|
||||
// cases are less common.
|
||||
// This type is also useful for storing references to apartment affine objects.
|
||||
//
|
||||
// Note, that apartment variables hosted in a COM DLL need to integrate with
|
||||
// the DllCanUnloadNow() function to include the ref counts contributed by
|
||||
// C++ WinRT objects. This is automatic for DLLs that host C++ WinRT objects
|
||||
// but WRL projects will need to be updated to call winrt::get_module_lock().
|
||||
|
||||
template<typename T, apartment_variable_leak_action leak_action = apartment_variable_leak_action::fail_fast,
|
||||
typename test_hook = wil::apartment_variable_platform>
|
||||
struct apartment_variable : details::apartment_variable_base<leak_action, test_hook>
|
||||
{
|
||||
using base = details::apartment_variable_base<leak_action, test_hook>;
|
||||
|
||||
constexpr apartment_variable() = default;
|
||||
|
||||
// Get current value or throw if no value has been set.
|
||||
T& get_existing() { return std::any_cast<T&>(base::get_existing()); }
|
||||
|
||||
// Get current value or default-construct one on demand.
|
||||
T& get_or_create()
|
||||
{
|
||||
return std::any_cast<T&>(base::get_or_create(details::any_maker<T>()));
|
||||
}
|
||||
|
||||
// Get current value or custom-construct one on demand.
|
||||
template<typename F>
|
||||
T& get_or_create(F&& f)
|
||||
{
|
||||
return std::any_cast<T&>(base::get_or_create(details::any_maker<T>(std::forward<F>(f))));
|
||||
}
|
||||
|
||||
// get pointer to current value or nullptr if no value has been set
|
||||
T* get_if() { return std::any_cast<T>(base::get_if()); }
|
||||
|
||||
// replace or create the current value, fail fasts if the value is not already stored
|
||||
template<typename V> void set(V&& value) { return base::set(std::forward<V>(value)); }
|
||||
|
||||
// Clear the value in the current apartment.
|
||||
using base::clear;
|
||||
|
||||
// Asynchronously clear the value in all apartments it is present in.
|
||||
using base::clear_all_apartments_async;
|
||||
|
||||
// For testing only.
|
||||
// 1) To observe the state of the storage in the debugger assign this to
|
||||
// a temporary variable (const&) and watch its contents.
|
||||
// 2) Use this to test the implementation.
|
||||
using base::storage;
|
||||
// For testing only. The number of variables in the current apartment.
|
||||
using base::current_apartment_variable_count;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // __WIL_COM_APARTMENT_VARIABLE_INCLUDED
|
||||
Vendored
-798
File diff suppressed because it is too large
Load Diff
Vendored
-408
@@ -1,408 +0,0 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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 <inspectable.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...
|
||||
// For example: "2.0.221104.6"
|
||||
inline constexpr int version_from_string(const char* versionString)
|
||||
{
|
||||
int result = 0;
|
||||
while ((*versionString >= '0') && (*versionString <= '9'))
|
||||
{
|
||||
result = result * 10 + (*versionString - '0');
|
||||
++versionString;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline constexpr int major_version_from_string(const char* versionString)
|
||||
{
|
||||
return version_from_string(versionString);
|
||||
}
|
||||
|
||||
inline constexpr int minor_version_from_string(const char* versionString)
|
||||
{
|
||||
int dotCount = 0;
|
||||
while ((*versionString != '\0'))
|
||||
{
|
||||
if (*versionString == '.')
|
||||
{
|
||||
++dotCount;
|
||||
}
|
||||
|
||||
++versionString;
|
||||
if (dotCount == 2)
|
||||
{
|
||||
return version_from_string(versionString);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/// @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;
|
||||
|
||||
// The same is true with this function pointer as well, except that the version must be 2.X or higher.
|
||||
extern void(__stdcall* winrt_throw_hresult_handler)(uint32_t, char const*, char const*, void*, winrt::hresult const) 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.to_abi();
|
||||
}
|
||||
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.to_abi();
|
||||
}
|
||||
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 __stdcall winrt_throw_hresult(uint32_t lineNumber, char const* fileName, char const* functionName, void* returnAddress, winrt::hresult const result) noexcept
|
||||
{
|
||||
void* callerReturnAddress{nullptr}; PCSTR code{nullptr};
|
||||
wil::details::ReportFailure_Hr<FailureType::Log>(__R_FN_CALL_FULL __R_COMMA result);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if constexpr (details::minor_version_from_string(CPPWINRT_VERSION) >= 210122)
|
||||
{
|
||||
WI_ASSERT(winrt_throw_hresult_handler == nullptr);
|
||||
winrt_throw_hresult_handler = winrt_throw_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));
|
||||
}
|
||||
|
||||
inline auto str_raw_ptr(const winrt::hstring& str) noexcept
|
||||
{
|
||||
return str.c_str();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
inline ::IUnknown* com_raw_ptr(const winrt::Windows::Foundation::IUnknown& ptr) noexcept
|
||||
{
|
||||
return static_cast<::IUnknown*>(winrt::get_abi(ptr));
|
||||
}
|
||||
|
||||
// Needed to power wil::cx_object_from_abi that requires IInspectable
|
||||
inline ::IInspectable* com_raw_ptr(const winrt::Windows::Foundation::IInspectable& ptr) noexcept
|
||||
{
|
||||
return static_cast<::IInspectable*>(winrt::get_abi(ptr));
|
||||
}
|
||||
|
||||
// Taken from the docs.microsoft.com article
|
||||
template <typename T>
|
||||
T convert_from_abi(::IUnknown* from)
|
||||
{
|
||||
T to{ nullptr }; // `T` is a projected type.
|
||||
winrt::check_hresult(from->QueryInterface(winrt::guid_of<T>(), winrt::put_abi(to)));
|
||||
return to;
|
||||
}
|
||||
|
||||
// For obtaining an object from an interop method on the factory. Example:
|
||||
// winrt::InputPane inputPane = wil::capture_interop<winrt::InputPane>(&IInputPaneInterop::GetForWindow, hwnd);
|
||||
// If the method produces something different from the factory type:
|
||||
// winrt::IAsyncAction action = wil::capture_interop<winrt::IAsyncAction, winrt::AccountsSettingsPane>(&IAccountsSettingsPaneInterop::ShowAddAccountForWindow, hwnd);
|
||||
template<typename WinRTResult, typename WinRTFactory = WinRTResult, typename Interface, typename... InterfaceArgs, typename... Args>
|
||||
auto capture_interop(HRESULT(__stdcall Interface::* method)(InterfaceArgs...), Args&&... args)
|
||||
{
|
||||
auto interop = winrt::get_activation_factory<WinRTFactory, Interface>();
|
||||
return winrt::capture<WinRTResult>(interop, method, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// For obtaining an object from an interop method on an instance. Example:
|
||||
// winrt::UserActivitySession session = wil::capture_interop<winrt::UserActivitySession>(activity, &IUserActivityInterop::CreateSessionForWindow, hwnd);
|
||||
template<typename WinRTResult, typename Interface, typename... InterfaceArgs, typename... Args>
|
||||
auto capture_interop(winrt::Windows::Foundation::IUnknown const& o, HRESULT(__stdcall Interface::* method)(InterfaceArgs...), Args&&... args)
|
||||
{
|
||||
return winrt::capture<WinRTResult>(o.as<Interface>(), method, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/** Holds a reference to the host C++/WinRT module to prevent it from being unloaded.
|
||||
Normally, this is done by being in an IAsyncOperation coroutine or by holding a strong
|
||||
reference to a C++/WinRT object hosted in the same module, but if you have neither,
|
||||
you will need to hold a reference explicitly. For the WRL equivalent, see wrl_module_reference.
|
||||
|
||||
This can be used as a base, which permits EBO:
|
||||
~~~~
|
||||
struct NonWinrtObject : wil::winrt_module_reference
|
||||
{
|
||||
int value;
|
||||
};
|
||||
|
||||
// DLL will not be unloaded as long as NonWinrtObject is still alive.
|
||||
auto p = std::make_unique<NonWinrtObject>();
|
||||
~~~~
|
||||
|
||||
Or it can be used as a member (with [[no_unique_address]] to avoid
|
||||
occupying any memory):
|
||||
~~~~
|
||||
struct NonWinrtObject
|
||||
{
|
||||
int value;
|
||||
|
||||
[[no_unique_address]] wil::winrt_module_reference module_ref;
|
||||
};
|
||||
|
||||
// DLL will not be unloaded as long as NonWinrtObject is still alive.
|
||||
auto p = std::make_unique<NonWinrtObject>();
|
||||
~~~~
|
||||
|
||||
If using it to prevent the host DLL from unloading while a thread
|
||||
or threadpool work item is still running, create the object before
|
||||
starting the thread, and pass it to the thread. This avoids a race
|
||||
condition where the host DLL could get unloaded before the thread starts.
|
||||
~~~~
|
||||
std::thread([module_ref = wil::winrt_module_reference()]() { do_background_work(); });
|
||||
|
||||
// Don't do this (race condition)
|
||||
std::thread([]() { wil::winrt_module_reference module_ref; do_background_work(); }); // WRONG
|
||||
~~~~
|
||||
|
||||
Also useful in coroutines that neither capture DLL-hosted COM objects, nor are themselves
|
||||
DLL-hosted COM objects. (If the coroutine returns IAsyncAction or captures a get_strong()
|
||||
of its containing WinRT class, then the IAsyncAction or strong reference will itself keep
|
||||
a strong reference to the host module.)
|
||||
~~~~
|
||||
winrt::fire_and_forget ContinueBackgroundWork()
|
||||
{
|
||||
// prevent DLL from unloading while we are running on a background thread.
|
||||
// Do this before switching to the background thread.
|
||||
wil::winrt_module_reference module_ref;
|
||||
|
||||
co_await winrt::resume_background();
|
||||
do_background_work();
|
||||
};
|
||||
~~~~
|
||||
*/
|
||||
struct [[nodiscard]] winrt_module_reference
|
||||
{
|
||||
winrt_module_reference()
|
||||
{
|
||||
++winrt::get_module_lock();
|
||||
}
|
||||
|
||||
winrt_module_reference(winrt_module_reference const&) : winrt_module_reference() {}
|
||||
|
||||
~winrt_module_reference()
|
||||
{
|
||||
--winrt::get_module_lock();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif // __WIL_CPPWINRT_INCLUDED
|
||||
-352
@@ -1,352 +0,0 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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_HELPERS_DEFINED
|
||||
#define __WIL_CPPWINRT_HELPERS_DEFINED
|
||||
|
||||
/// @cond
|
||||
namespace wil::details
|
||||
{
|
||||
struct dispatcher_RunAsync
|
||||
{
|
||||
template<typename Dispatcher, typename... Args>
|
||||
static void Schedule(Dispatcher const& dispatcher, Args&&... args)
|
||||
{
|
||||
dispatcher.RunAsync(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
struct dispatcher_TryEnqueue
|
||||
{
|
||||
template<typename Dispatcher, typename... Args>
|
||||
static void Schedule(Dispatcher const& dispatcher, Args&&... args)
|
||||
{
|
||||
dispatcher.TryEnqueue(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Dispatcher> struct dispatcher_traits;
|
||||
}
|
||||
|
||||
#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
|
||||
#include <experimental/coroutine>
|
||||
namespace wil::details
|
||||
{
|
||||
template<typename T = void> using coroutine_handle = std::experimental::coroutine_handle<T>;
|
||||
}
|
||||
#elif defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L)
|
||||
#include <coroutine>
|
||||
namespace wil::details
|
||||
{
|
||||
template<typename T = void> using coroutine_handle = std::coroutine_handle<T>;
|
||||
}
|
||||
#endif
|
||||
/// @endcond
|
||||
|
||||
#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED) || (defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L))
|
||||
/// @cond
|
||||
namespace wil::details
|
||||
{
|
||||
struct dispatched_handler_state
|
||||
{
|
||||
details::coroutine_handle<> handle{};
|
||||
bool orphaned = false;
|
||||
};
|
||||
|
||||
struct dispatcher_handler
|
||||
{
|
||||
dispatcher_handler(dispatched_handler_state* state) : m_state(state) { }
|
||||
dispatcher_handler(dispatcher_handler&& other) noexcept : m_state(std::exchange(other.m_state, {})) {}
|
||||
|
||||
~dispatcher_handler()
|
||||
{
|
||||
if (m_state && m_state->handle)
|
||||
{
|
||||
m_state->orphaned = true;
|
||||
Complete();
|
||||
}
|
||||
}
|
||||
void operator()()
|
||||
{
|
||||
Complete();
|
||||
}
|
||||
|
||||
void Complete()
|
||||
{
|
||||
auto state = std::exchange(m_state, nullptr);
|
||||
std::exchange(state->handle, {}).resume();
|
||||
}
|
||||
|
||||
dispatched_handler_state* m_state;
|
||||
};
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
namespace wil
|
||||
{
|
||||
//! Resumes coroutine execution on the thread associated with the dispatcher, or throws
|
||||
//! an exception (from an arbitrary thread) if unable. Supported dispatchers are
|
||||
//! Windows.System.DispatcherQueue, Microsoft.System.DispatcherQueue,
|
||||
//! Microsoft.UI.Dispatching.DispatcherQueue, and Windows.UI.Core.CoreDispatcher,
|
||||
//! but you must include the corresponding <winrt/Namespace.h> header before including
|
||||
//! wil\cppwinrt_helpers.h. It is okay to include wil\cppwinrt_helpers.h multiple times:
|
||||
//! support will be enabled for any winrt/Namespace.h headers that were included since
|
||||
//! the previous inclusion of wil\cppwinrt_headers.h.
|
||||
template<typename Dispatcher>
|
||||
[[nodiscard]] auto resume_foreground(Dispatcher const& dispatcher,
|
||||
typename details::dispatcher_traits<Dispatcher>::Priority priority = details::dispatcher_traits<Dispatcher>::Priority::Normal)
|
||||
{
|
||||
using Traits = details::dispatcher_traits<Dispatcher>;
|
||||
using Priority = typename Traits::Priority;
|
||||
using Handler = typename Traits::Handler;
|
||||
|
||||
struct awaitable
|
||||
{
|
||||
awaitable(Dispatcher const& dispatcher, Priority priority) noexcept :
|
||||
m_dispatcher(dispatcher),
|
||||
m_priority(priority)
|
||||
{
|
||||
}
|
||||
bool await_ready() const noexcept { return false; }
|
||||
|
||||
void await_suspend(details::coroutine_handle<> handle)
|
||||
{
|
||||
m_state.handle = handle;
|
||||
Handler handler{ details::dispatcher_handler(&m_state) };
|
||||
try
|
||||
{
|
||||
// The return value of Schedule is not reliable. Use the dispatcher_handler destructor
|
||||
// to detect whether the work item failed to run.
|
||||
Traits::Scheduler::Schedule(m_dispatcher, m_priority, handler);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
m_state.handle = nullptr; // the exception will resume the coroutine, so the handler shouldn't do it
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void await_resume() const
|
||||
{
|
||||
if (m_state.orphaned)
|
||||
{
|
||||
throw winrt::hresult_error(static_cast<winrt::hresult>(0x800701ab)); // HRESULT_FROM_WIN32(ERROR_NO_TASK_QUEUE)
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Dispatcher const& m_dispatcher;
|
||||
Priority const m_priority;
|
||||
details::dispatched_handler_state m_state;
|
||||
};
|
||||
return awaitable{ dispatcher, priority };
|
||||
}
|
||||
}
|
||||
#endif // Coroutines are supported
|
||||
|
||||
#endif // __WIL_CPPWINRT_HELPERS_DEFINED
|
||||
|
||||
/// @cond
|
||||
#if defined(WINRT_Windows_UI_Core_H) && !defined(__WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS)
|
||||
#define __WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS
|
||||
namespace wil::details
|
||||
{
|
||||
template<>
|
||||
struct dispatcher_traits<winrt::Windows::UI::Core::CoreDispatcher>
|
||||
{
|
||||
using Priority = winrt::Windows::UI::Core::CoreDispatcherPriority;
|
||||
using Handler = winrt::Windows::UI::Core::DispatchedHandler;
|
||||
using Scheduler = dispatcher_RunAsync;
|
||||
};
|
||||
}
|
||||
#endif // __WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS
|
||||
|
||||
#if defined(WINRT_Windows_System_H) && !defined(__WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS)
|
||||
#define __WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS
|
||||
namespace wil::details
|
||||
{
|
||||
template<>
|
||||
struct dispatcher_traits<winrt::Windows::System::DispatcherQueue>
|
||||
{
|
||||
using Priority = winrt::Windows::System::DispatcherQueuePriority;
|
||||
using Handler = winrt::Windows::System::DispatcherQueueHandler;
|
||||
using Scheduler = dispatcher_TryEnqueue;
|
||||
};
|
||||
}
|
||||
#endif // __WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS
|
||||
|
||||
#if defined(WINRT_Microsoft_System_H) && !defined(__WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS)
|
||||
#define __WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS
|
||||
namespace wil::details
|
||||
{
|
||||
template<>
|
||||
struct dispatcher_traits<winrt::Microsoft::System::DispatcherQueue>
|
||||
{
|
||||
using Priority = winrt::Microsoft::System::DispatcherQueuePriority;
|
||||
using Handler = winrt::Microsoft::System::DispatcherQueueHandler;
|
||||
using Scheduler = dispatcher_TryEnqueue;
|
||||
};
|
||||
}
|
||||
#endif // __WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS
|
||||
|
||||
#if defined(WINRT_Microsoft_UI_Dispatching_H) && !defined(__WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS)
|
||||
#define __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS
|
||||
namespace wil::details
|
||||
{
|
||||
template<>
|
||||
struct dispatcher_traits<winrt::Microsoft::UI::Dispatching::DispatcherQueue>
|
||||
{
|
||||
using Priority = winrt::Microsoft::UI::Dispatching::DispatcherQueuePriority;
|
||||
using Handler = winrt::Microsoft::UI::Dispatching::DispatcherQueueHandler;
|
||||
using Scheduler = dispatcher_TryEnqueue;
|
||||
};
|
||||
}
|
||||
#endif // __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS
|
||||
/// @endcond
|
||||
|
||||
#if defined(WINRT_Windows_Foundation_Collections_H) && !defined(__WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS)
|
||||
#define __WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS
|
||||
namespace wil
|
||||
{
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
template<typename T> struct is_winrt_vector_like {
|
||||
private:
|
||||
template <typename U,
|
||||
typename = decltype(std::declval<U>().GetMany(std::declval<U>().Size(),
|
||||
winrt::array_view<decltype(std::declval<U>().GetAt(0))>{}))>
|
||||
static constexpr bool get_value(int) { return true; }
|
||||
template <typename> static constexpr bool get_value(...) { return false; }
|
||||
public:
|
||||
static constexpr bool value = get_value<T>(0);
|
||||
};
|
||||
|
||||
template<typename T> struct is_winrt_iterator_like {
|
||||
private:
|
||||
template <typename U,
|
||||
typename = decltype(std::declval<U>().GetMany(winrt::array_view<decltype(std::declval<U>().Current())>{}))>
|
||||
static constexpr bool get_value(int) { return true; }
|
||||
template <typename> static constexpr bool get_value(...) { return false; }
|
||||
public:
|
||||
static constexpr bool value = get_value<T>(0);
|
||||
};
|
||||
|
||||
template<typename T> constexpr T empty() noexcept
|
||||
{
|
||||
if constexpr (std::is_base_of_v<winrt::Windows::Foundation::IUnknown, T>)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
/** Converts C++ / WinRT vectors, iterators, and iterables to std::vector by requesting the
|
||||
collection's data in bulk. This can be more efficient in terms of IPC cost than iteratively
|
||||
processing the collection.
|
||||
~~~
|
||||
winrt::IVector<winrt::hstring> collection = GetCollection();
|
||||
std::vector<winrt::hstring> allData = wil::to_vector(collection); // read all data from collection
|
||||
for (winrt::hstring const& item : allData)
|
||||
{
|
||||
// use item
|
||||
}
|
||||
~~~
|
||||
Can be used for IVector<T>, IVectorView<T>, IIterable<T>, IIterator<T>, and any type or
|
||||
interface that C++/WinRT projects those interfaces for (PropertySet, IMap<T,K>, etc.)
|
||||
Iterable-only types fetch content in units of 64. When used with an iterator, the returned
|
||||
vector contains the iterator's current position and any others after it.
|
||||
*/
|
||||
template<typename TSrc> auto to_vector(TSrc const& src)
|
||||
{
|
||||
if constexpr (details::is_winrt_vector_like<TSrc>::value)
|
||||
{
|
||||
using T = decltype(src.GetAt(0));
|
||||
std::vector<T> result;
|
||||
if (auto expected = src.Size())
|
||||
{
|
||||
result.resize(expected + 1, details::empty<T>());
|
||||
auto actual = src.GetMany(0, result);
|
||||
if (actual > expected)
|
||||
{
|
||||
throw winrt::hresult_changed_state();
|
||||
}
|
||||
result.resize(actual);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else if constexpr (details::is_winrt_iterator_like<TSrc>::value)
|
||||
{
|
||||
using T = decltype(src.Current());
|
||||
std::vector<T> result;
|
||||
constexpr uint32_t chunkSize = 64;
|
||||
while (true)
|
||||
{
|
||||
auto const lastSize = result.size();
|
||||
result.resize(lastSize + chunkSize, details::empty<T>());
|
||||
auto fetched = src.GetMany({result.data() + lastSize, result.data() + lastSize + chunkSize });
|
||||
if (fetched < chunkSize)
|
||||
{
|
||||
result.resize(lastSize + fetched);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return to_vector(src.First());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(WINRT_Windows_UI_H) && defined(_WINDOWS_UI_INTEROP_H_) && !defined(__WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS)
|
||||
#define __WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS
|
||||
#if !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) && !defined(MIDL_NS_PREFIX)
|
||||
#pragma push_macro("ABI")
|
||||
#undef ABI
|
||||
#define ABI
|
||||
#endif
|
||||
|
||||
namespace wil
|
||||
{
|
||||
#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_CU)
|
||||
//! The following methods require that you include both <winrt/Windows.UI.h>
|
||||
//! <Windows.UI.Interop.h> before including wil/cppwinrt_helpers.h, and that NTDDI_VERSION
|
||||
//! is at least NTDDI_WIN10_CU. It is okay to include wil\cppwinrt_helpers.h multiple times:
|
||||
//! support will be enabled for any headers that were included since the previous inclusion
|
||||
//! of wil\cppwinrt_headers.h.
|
||||
inline winrt::Windows::UI::WindowId GetWindowIdFromWindow(HWND hwnd)
|
||||
{
|
||||
ABI::Windows::UI::WindowId abiWindowId;
|
||||
winrt::check_hresult(::GetWindowIdFromWindow(hwnd, &abiWindowId));
|
||||
return winrt::Windows::UI::WindowId{ abiWindowId.Value };
|
||||
}
|
||||
|
||||
inline HWND GetWindowFromWindowId(winrt::Windows::UI::WindowId windowId)
|
||||
{
|
||||
HWND hwnd;
|
||||
winrt::check_hresult(::GetWindowFromWindowId({ windowId.Value }, &hwnd));
|
||||
return hwnd;
|
||||
}
|
||||
#endif /*defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_CU)*/
|
||||
}
|
||||
|
||||
#if !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) && !defined(MIDL_NS_PREFIX)
|
||||
#pragma pop_macro("ABI")
|
||||
#endif
|
||||
#endif // __WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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_WRL_INCLUDED
|
||||
#define __WIL_CPPWINRT_WRL_INCLUDED
|
||||
|
||||
#include "cppwinrt.h"
|
||||
#include <winrt\base.h>
|
||||
|
||||
#include "result_macros.h"
|
||||
#include <wrl\module.h>
|
||||
|
||||
// wil::wrl_factory_for_winrt_com_class provides interopability between a
|
||||
// C++/WinRT class and the WRL Module system, allowing the winrt class to be
|
||||
// CoCreatable.
|
||||
//
|
||||
// Usage:
|
||||
// - In your cpp, add:
|
||||
// CoCreatableCppWinRtClass(className)
|
||||
//
|
||||
// - In the dll.cpp (or equivalent) for the module containing your class, add:
|
||||
// CoCreatableClassWrlCreatorMapInclude(className)
|
||||
//
|
||||
namespace wil
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
template <typename TCppWinRTClass>
|
||||
class module_count_wrapper : public TCppWinRTClass
|
||||
{
|
||||
public:
|
||||
module_count_wrapper()
|
||||
{
|
||||
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
|
||||
{
|
||||
modulePtr->IncrementObjectCount();
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~module_count_wrapper()
|
||||
{
|
||||
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
|
||||
{
|
||||
modulePtr->DecrementObjectCount();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <typename TCppWinRTClass>
|
||||
class wrl_factory_for_winrt_com_class : public ::Microsoft::WRL::ClassFactory<>
|
||||
{
|
||||
public:
|
||||
IFACEMETHODIMP CreateInstance(_In_opt_ ::IUnknown* unknownOuter, REFIID riid, _COM_Outptr_ void **object) noexcept try
|
||||
{
|
||||
*object = nullptr;
|
||||
RETURN_HR_IF(CLASS_E_NOAGGREGATION, unknownOuter != nullptr);
|
||||
|
||||
return winrt::make<details::module_count_wrapper<TCppWinRTClass>>().as(riid, object);
|
||||
}
|
||||
CATCH_RETURN()
|
||||
};
|
||||
}
|
||||
|
||||
#define CoCreatableCppWinRtClass(className) CoCreatableClassWithFactory(className, ::wil::wrl_factory_for_winrt_com_class<className>)
|
||||
|
||||
#endif // __WIL_CPPWINRT_WRL_INCLUDED
|
||||
-1048
File diff suppressed because it is too large
Load Diff
-168
@@ -1,168 +0,0 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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_NT_RESULTMACROS_INCLUDED
|
||||
#define __WIL_NT_RESULTMACROS_INCLUDED
|
||||
|
||||
#include "result_macros.h"
|
||||
|
||||
// Helpers for return macros
|
||||
#define __NT_RETURN_NTSTATUS(status, str) __WI_SUPPRESS_4127_S do { NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { __R_FN(Return_NtStatus)(__R_INFO(str) __status); } return __status; } __WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
#define __NT_RETURN_NTSTATUS_MSG(status, str, fmt, ...) __WI_SUPPRESS_4127_S do { NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, ##__VA_ARGS__); } return __status; } __WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
|
||||
//*****************************************************************************
|
||||
// Macros for returning failures as NTSTATUS
|
||||
//*****************************************************************************
|
||||
|
||||
// Always returns a known result (NTSTATUS) - always logs failures
|
||||
#define NT_RETURN_NTSTATUS(status) __NT_RETURN_NTSTATUS(wil::verify_ntstatus(status), #status)
|
||||
|
||||
// Always returns a known failure (NTSTATUS) - always logs a var-arg message on failure
|
||||
#define NT_RETURN_NTSTATUS_MSG(status, fmt, ...) __NT_RETURN_NTSTATUS_MSG(wil::verify_ntstatus(status), #status, fmt, ##__VA_ARGS__)
|
||||
|
||||
// Conditionally returns failures (NTSTATUS) - always logs failures
|
||||
#define NT_RETURN_IF_NTSTATUS_FAILED(status) __WI_SUPPRESS_4127_S do { const auto __statusRet = wil::verify_ntstatus(status); if (FAILED_NTSTATUS(__statusRet)) { __NT_RETURN_NTSTATUS(__statusRet, #status); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||
|
||||
// Conditionally returns failures (NTSTATUS) - always logs a var-arg message on failure
|
||||
#define NT_RETURN_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) __WI_SUPPRESS_4127_S do { const auto __statusRet = wil::verify_ntstatus(status); if (FAILED_NTSTATUS(__statusRet)) { __NT_RETURN_NTSTATUS_MSG(__statusRet, #status, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0)
|
||||
|
||||
//*****************************************************************************
|
||||
// Macros to catch and convert exceptions on failure
|
||||
//*****************************************************************************
|
||||
|
||||
// Use these macros *within* a catch (...) block to handle exceptions
|
||||
#define NT_RETURN_CAUGHT_EXCEPTION() return __R_FN(Nt_Return_CaughtException)(__R_INFO_ONLY(nullptr))
|
||||
#define NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ...) return __R_FN(Nt_Return_CaughtExceptionMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__)
|
||||
|
||||
// Use these macros in place of a catch block to handle exceptions
|
||||
#define NT_CATCH_RETURN() catch (...) { NT_RETURN_CAUGHT_EXCEPTION(); }
|
||||
#define NT_CATCH_RETURN_MSG(fmt, ...) catch (...) { NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); }
|
||||
|
||||
|
||||
namespace wil
|
||||
{
|
||||
//*****************************************************************************
|
||||
// Public Helpers that catch -- mostly only enabled when exceptions are enabled
|
||||
//*****************************************************************************
|
||||
|
||||
// StatusFromCaughtException is a function that is meant to be called from within a catch(...) block. Internally
|
||||
// it re-throws and catches the exception to convert it to an NTSTATUS. If an exception is of an unrecognized type
|
||||
// the function will fail fast.
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// // Code
|
||||
// }
|
||||
// catch (...)
|
||||
// {
|
||||
// status = wil::StatusFromCaughtException();
|
||||
// }
|
||||
_Always_(_Post_satisfies_(return < 0))
|
||||
__declspec(noinline) inline NTSTATUS StatusFromCaughtException() WI_NOEXCEPT
|
||||
{
|
||||
bool isNormalized = false;
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
if (details::g_pfnResultFromCaughtExceptionInternal)
|
||||
{
|
||||
status = details::g_pfnResultFromCaughtExceptionInternal(nullptr, 0, &isNormalized).status;
|
||||
}
|
||||
if (FAILED_NTSTATUS(status))
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
// Caller bug: an unknown exception was thrown
|
||||
__WIL_PRIVATE_FAIL_FAST_HR_IF(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION), g_fResultFailFastUnknownExceptions);
|
||||
return wil::details::HrToNtStatus(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
|
||||
}
|
||||
|
||||
namespace details
|
||||
{
|
||||
template<FailureType>
|
||||
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported = SupportedExceptions::Default);
|
||||
template<FailureType>
|
||||
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList);
|
||||
|
||||
namespace __R_NS_NAME
|
||||
{
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
__R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT
|
||||
{
|
||||
__R_FN_LOCALS;
|
||||
return wil::details::ReportStatus_CaughtException<FailureType::Return>(__R_DIRECT_FN_CALL_ONLY);
|
||||
}
|
||||
|
||||
__R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtExceptionMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT
|
||||
{
|
||||
va_list argList;
|
||||
va_start(argList, formatString);
|
||||
__R_FN_LOCALS;
|
||||
return wil::details::ReportStatus_CaughtExceptionMsg<FailureType::Return>(__R_DIRECT_FN_CALL formatString, argList);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template<FailureType T>
|
||||
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported)
|
||||
{
|
||||
wchar_t message[2048];
|
||||
message[0] = L'\0';
|
||||
return ReportFailure_CaughtExceptionCommon<T>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status;
|
||||
}
|
||||
|
||||
template<>
|
||||
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException<FailureType::FailFast>(__R_FN_PARAMS_FULL, SupportedExceptions supported)
|
||||
{
|
||||
wchar_t message[2048];
|
||||
message[0] = L'\0';
|
||||
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::FailFast>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status);
|
||||
}
|
||||
|
||||
template<>
|
||||
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException<FailureType::Exception>(__R_FN_PARAMS_FULL, SupportedExceptions supported)
|
||||
{
|
||||
wchar_t message[2048];
|
||||
message[0] = L'\0';
|
||||
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::Exception>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status);
|
||||
}
|
||||
|
||||
template<FailureType T>
|
||||
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList)
|
||||
{
|
||||
// Pre-populate the buffer with our message, the exception message will be added to it...
|
||||
wchar_t message[2048];
|
||||
PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList);
|
||||
StringCchCatW(message, ARRAYSIZE(message), L" -- ");
|
||||
return ReportFailure_CaughtExceptionCommon<T>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status;
|
||||
}
|
||||
|
||||
template<>
|
||||
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg<FailureType::FailFast>(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList)
|
||||
{
|
||||
// Pre-populate the buffer with our message, the exception message will be added to it...
|
||||
wchar_t message[2048];
|
||||
PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList);
|
||||
StringCchCatW(message, ARRAYSIZE(message), L" -- ");
|
||||
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::FailFast>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status);
|
||||
}
|
||||
|
||||
template<>
|
||||
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg<FailureType::Exception>(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList)
|
||||
{
|
||||
// Pre-populate the buffer with our message, the exception message will be added to it...
|
||||
wchar_t message[2048];
|
||||
PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList);
|
||||
StringCchCatW(message, ARRAYSIZE(message), L" -- ");
|
||||
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::Exception>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __WIL_NT_RESULTMACROS_INCLUDED
|
||||
Vendored
-277
@@ -1,277 +0,0 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// 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(®istry_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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user