mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 06:09:50 -06:00
Add Windows Implementation Libraries
This commit is contained in:
2832
Externals/WIL/include/wil/com.h
vendored
Normal file
2832
Externals/WIL/include/wil/com.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
747
Externals/WIL/include/wil/common.h
vendored
Normal file
747
Externals/WIL/include/wil/common.h
vendored
Normal file
@ -0,0 +1,747 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#ifndef __WIL_COMMON_INCLUDED
|
||||
#define __WIL_COMMON_INCLUDED
|
||||
|
||||
#if defined(_KERNEL_MODE ) && !defined(__WIL_MIN_KERNEL)
|
||||
// This define indicates that the WIL usage is in a kernel mode context where
|
||||
// a high degree of WIL functionality is desired.
|
||||
//
|
||||
// Use (sparingly) to change behavior based on whether WIL is being used in kernel
|
||||
// mode or user mode.
|
||||
#define WIL_KERNEL_MODE
|
||||
#endif
|
||||
|
||||
// Defining WIL_HIDE_DEPRECATED will hide everything deprecated.
|
||||
// Each wave of deprecation will add a new WIL_HIDE_DEPRECATED_YYMM number that can be used to lock deprecation at
|
||||
// a particular point, allowing components to avoid backslide and catch up to the current independently.
|
||||
#ifdef WIL_HIDE_DEPRECATED
|
||||
#define WIL_HIDE_DEPRECATED_1809
|
||||
#endif
|
||||
#ifdef WIL_HIDE_DEPRECATED_1809
|
||||
#define WIL_HIDE_DEPRECATED_1612
|
||||
#endif
|
||||
#ifdef WIL_HIDE_DEPRECATED_1612
|
||||
#define WIL_HIDE_DEPRECATED_1611
|
||||
#endif
|
||||
|
||||
// Implementation side note: ideally the deprecation would be done with the function-level declspec
|
||||
// as it allows you to utter the error text when used. The declspec works, but doing it selectively with
|
||||
// a macro makes intellisense deprecation comments not work. So we just use the #pragma deprecation.
|
||||
#ifdef WIL_WARN_DEPRECATED
|
||||
#define WIL_WARN_DEPRECATED_1809
|
||||
#endif
|
||||
#ifdef WIL_WARN_DEPRECATED_1809
|
||||
#define WIL_WARN_DEPRECATED_1612
|
||||
#endif
|
||||
#ifdef WIL_WARN_DEPRECATED_1612
|
||||
#define WIL_WARN_DEPRECATED_1611
|
||||
#endif
|
||||
#ifdef WIL_WARN_DEPRECATED_1809
|
||||
#define WIL_WARN_DEPRECATED_1809_PRAGMA(...) __pragma(deprecated(__VA_ARGS__))
|
||||
#else
|
||||
#define WIL_WARN_DEPRECATED_1809_PRAGMA(...)
|
||||
#endif
|
||||
#ifdef WIL_WARN_DEPRECATED_1611
|
||||
#define WIL_WARN_DEPRECATED_1611_PRAGMA(...) __pragma(deprecated(__VA_ARGS__))
|
||||
#else
|
||||
#define WIL_WARN_DEPRECATED_1611_PRAGMA(...)
|
||||
#endif
|
||||
#ifdef WIL_WARN_DEPRECATED_1612
|
||||
#define WIL_WARN_DEPRECATED_1612_PRAGMA(...) __pragma(deprecated(__VA_ARGS__))
|
||||
#else
|
||||
#define WIL_WARN_DEPRECATED_1612_PRAGMA(...)
|
||||
#endif
|
||||
|
||||
#if defined(_MSVC_LANG)
|
||||
#define __WI_SUPPRESS_4127_S __pragma(warning(push)) __pragma(warning(disable:4127)) __pragma(warning(disable:26498)) __pragma(warning(disable:4245))
|
||||
#define __WI_SUPPRESS_4127_E __pragma(warning(pop))
|
||||
#define __WI_SUPPRESS_NULLPTR_ANALYSIS __pragma(warning(suppress:28285)) __pragma(warning(suppress:6504))
|
||||
#define __WI_SUPPRESS_NONINIT_ANALYSIS __pragma(warning(suppress:26495))
|
||||
#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS __pragma(warning(suppress:26439))
|
||||
#else
|
||||
#define __WI_SUPPRESS_4127_S
|
||||
#define __WI_SUPPRESS_4127_E
|
||||
#define __WI_SUPPRESS_NULLPTR_ANALYSIS
|
||||
#define __WI_SUPPRESS_NONINIT_ANALYSIS
|
||||
#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS
|
||||
#endif
|
||||
|
||||
#if !defined(__cplusplus) || defined(__WIL_MIN_KERNEL)
|
||||
|
||||
#define WI_ODR_PRAGMA(NAME, TOKEN)
|
||||
#define WI_NOEXCEPT
|
||||
|
||||
#else
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4714) // __forceinline not honored
|
||||
|
||||
// DO NOT add *any* further includes to this file -- there should be no dependencies from its usage
|
||||
#include <sal.h>
|
||||
#include "wistd_type_traits.h"
|
||||
|
||||
//! This macro inserts ODR violation protection; the macro allows it to be compatible with straight "C" code
|
||||
#define WI_ODR_PRAGMA(NAME, TOKEN) __pragma(detect_mismatch("ODR_violation_" NAME "_mismatch", TOKEN))
|
||||
|
||||
#ifdef WIL_KERNEL_MODE
|
||||
WI_ODR_PRAGMA("WIL_KERNEL_MODE", "1")
|
||||
#else
|
||||
WI_ODR_PRAGMA("WIL_KERNEL_MODE", "0")
|
||||
#endif
|
||||
|
||||
// Some SAL remapping / decoration to better support Doxygen. Macros that look like function calls can
|
||||
// confuse Doxygen when they are used to decorate a function or variable. We simplify some of these to
|
||||
// basic macros without the function for common use cases.
|
||||
/// @cond
|
||||
#define _Success_return_ _Success_(return)
|
||||
#define _Success_true_ _Success_(true)
|
||||
#define __declspec_noinline_ __declspec(noinline)
|
||||
#define __declspec_selectany_ __declspec(selectany)
|
||||
/// @endcond
|
||||
|
||||
#if defined(_CPPUNWIND) && !defined(WIL_SUPPRESS_EXCEPTIONS)
|
||||
/** This define is automatically set when exceptions are enabled within wil.
|
||||
It is automatically defined when your code is compiled with exceptions enabled (via checking for the built-in
|
||||
_CPPUNWIND flag) unless you explicitly define WIL_SUPPRESS_EXCEPTIONS ahead of including your first wil
|
||||
header. All exception-based WIL methods and classes are included behind:
|
||||
~~~~
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
// code
|
||||
#endif
|
||||
~~~~
|
||||
This enables exception-free code to directly include WIL headers without worrying about exception-based
|
||||
routines suddenly becoming available. */
|
||||
#define WIL_ENABLE_EXCEPTIONS
|
||||
#endif
|
||||
/// @endcond
|
||||
|
||||
/// @cond
|
||||
#if defined(WIL_EXCEPTION_MODE)
|
||||
static_assert(WIL_EXCEPTION_MODE <= 2, "Invalid exception mode");
|
||||
#elif !defined(WIL_LOCK_EXCEPTION_MODE)
|
||||
#define WIL_EXCEPTION_MODE 0 // default, can link exception-based and non-exception based libraries together
|
||||
#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "0")
|
||||
#elif defined(WIL_ENABLE_EXCEPTIONS)
|
||||
#define WIL_EXCEPTION_MODE 1 // new code optimization: ONLY support linking libraries together that have exceptions enabled
|
||||
#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "1")
|
||||
#else
|
||||
#define WIL_EXCEPTION_MODE 2 // old code optimization: ONLY support linking libraries that are NOT using exceptions
|
||||
#pragma detect_mismatch("ODR_violation_WIL_EXCEPTION_MODE_mismatch", "2")
|
||||
#endif
|
||||
|
||||
#if WIL_EXCEPTION_MODE == 1 && !defined(WIL_ENABLE_EXCEPTIONS)
|
||||
#error Must enable exceptions when WIL_EXCEPTION_MODE == 1
|
||||
#endif
|
||||
|
||||
// block for documentation only
|
||||
#if defined(WIL_DOXYGEN)
|
||||
/** This define can be explicitly set to disable exception usage within wil.
|
||||
Normally this define is never needed as the WIL_ENABLE_EXCEPTIONS macro is enabled automatically by looking
|
||||
at _CPPUNWIND. If your code compiles with exceptions enabled, but does not want to enable the exception-based
|
||||
classes and methods from WIL, define this macro ahead of including the first WIL header. */
|
||||
#define WIL_SUPPRESS_EXCEPTIONS
|
||||
|
||||
/** This define can be explicitly set to lock the process exception mode to WIL_ENABLE_EXCEPTIONS.
|
||||
Locking the exception mode provides optimizations to exception barriers, staging hooks and DLL load costs as it eliminates the need to
|
||||
do copy-on-write initialization of various function pointers and the necessary indirection that's done within WIL to avoid ODR violations
|
||||
when linking libraries together with different exception handling semantics. */
|
||||
#define WIL_LOCK_EXCEPTION_MODE
|
||||
|
||||
/** This define explicit sets the exception mode for the process to control optimizations.
|
||||
Three exception modes are available:
|
||||
0) This is the default. This enables a binary to link both exception-based and non-exception based libraries together that
|
||||
use WIL. This adds overhead to exception barriers, DLL copy on write pages and indirection through function pointers to avoid ODR
|
||||
violations when linking libraries together with different exception handling semantics.
|
||||
1) Prefer this setting when it can be used. This locks the binary to only supporting libraries which were built with exceptions enabled.
|
||||
2) This locks the binary to libraries built without exceptions. */
|
||||
#define WIL_EXCEPTION_MODE
|
||||
#endif
|
||||
|
||||
#if (__cplusplus >= 201703) || (_MSVC_LANG >= 201703)
|
||||
#define WIL_HAS_CXX_17 1
|
||||
#else
|
||||
#define WIL_HAS_CXX_17 0
|
||||
#endif
|
||||
|
||||
// Until we'll have C++17 enabled in our code base, we're falling back to SAL
|
||||
#define WI_NODISCARD __WI_LIBCPP_NODISCARD_ATTRIBUTE
|
||||
|
||||
//! @defgroup macrobuilding Macro Composition
|
||||
//! The following macros are building blocks primarily intended for authoring other macros.
|
||||
//! @{
|
||||
|
||||
//! Re-state a macro value (indirection for composition)
|
||||
#define WI_FLATTEN(...) __VA_ARGS__
|
||||
|
||||
/// @cond
|
||||
#define __WI_PASTE_imp(a, b) a##b
|
||||
/// @endcond
|
||||
|
||||
//! This macro is for use in other macros to paste two tokens together, such as a constant and the __LINE__ macro.
|
||||
#define WI_PASTE(a, b) __WI_PASTE_imp(a, b)
|
||||
|
||||
/// @cond
|
||||
#define __WI_HAS_VA_OPT_IMPL(F, T, ...) T
|
||||
#define __WI_HAS_VA_OPT_(...) __WI_HAS_VA_OPT_IMPL(__VA_OPT__(0,) 1, 0)
|
||||
/// @endcond
|
||||
|
||||
//! Evaluates to '1' when support for '__VA_OPT__' is available, else '0'
|
||||
#define WI_HAS_VA_OPT __WI_HAS_VA_OPT_(unused)
|
||||
|
||||
/// @cond
|
||||
#define __WI_ARGS_COUNT1(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, \
|
||||
A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, \
|
||||
A60, A61, A62, A63, A64, A65, A66, A67, A68, A69, A70, A71, A72, A73, A74, A75, A76, A77, A78, A79, A80, A81, A82, A83, A84, A85, A86, A87, A88, A89, \
|
||||
A90, A91, A92, A93, A94, A95, A96, A97, A98, A99, count, ...) count
|
||||
#define __WI_ARGS_COUNT0(...) WI_FLATTEN(__WI_ARGS_COUNT1(__VA_ARGS__, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, \
|
||||
79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, \
|
||||
39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
|
||||
#define __WI_ARGS_COUNT_PREFIX(...) 0, __VA_ARGS__
|
||||
/// @endcond
|
||||
|
||||
//! This variadic macro returns the number of arguments passed to it (up to 99).
|
||||
#if WI_HAS_VA_OPT
|
||||
#define WI_ARGS_COUNT(...) __WI_ARGS_COUNT0(0 __VA_OPT__(, __VA_ARGS__))
|
||||
#else
|
||||
#define WI_ARGS_COUNT(...) __WI_ARGS_COUNT0(__WI_ARGS_COUNT_PREFIX(__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
/// @cond
|
||||
#define __WI_FOR_imp0( fn)
|
||||
#define __WI_FOR_imp1( fn, arg) fn(arg)
|
||||
#define __WI_FOR_imp2( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp1(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp3( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp2(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp4( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp3(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp5( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp4(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp6( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp5(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp7( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp6(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp8( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp7(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp9( fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp8(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp10(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp9(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp11(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp10(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp12(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp11(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp13(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp12(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp14(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp13(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp15(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp14(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp16(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp15(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp17(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp16(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp18(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp17(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp19(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp18(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp20(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp19(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp21(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp20(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp22(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp21(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp23(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp22(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp24(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp23(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp25(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp24(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp26(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp25(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp27(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp26(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp28(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp27(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp29(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp28(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp30(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp29(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp31(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp30(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp32(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp31(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp33(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp32(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp34(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp33(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp35(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp34(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp36(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp35(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp37(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp36(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp38(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp37(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp39(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp38(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp40(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp39(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp41(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp40(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp42(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp41(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp43(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp42(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp44(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp43(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp45(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp44(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp46(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp45(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp47(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp46(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp48(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp47(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp49(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp48(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp50(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp49(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp51(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp50(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp52(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp51(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp53(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp52(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp54(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp53(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp55(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp54(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp56(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp55(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp57(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp56(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp58(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp57(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp59(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp58(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp60(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp59(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp61(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp60(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp62(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp61(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp63(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp62(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp64(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp63(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp65(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp64(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp66(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp65(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp67(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp66(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp68(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp67(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp69(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp68(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp70(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp69(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp71(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp70(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp72(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp71(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp73(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp72(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp74(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp73(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp75(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp74(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp76(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp75(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp77(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp76(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp78(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp77(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp79(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp78(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp80(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp79(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp81(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp80(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp82(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp81(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp83(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp82(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp84(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp83(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp85(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp84(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp86(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp85(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp87(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp86(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp88(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp87(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp89(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp88(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp90(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp89(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp91(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp90(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp92(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp91(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp93(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp92(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp94(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp93(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp95(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp94(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp96(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp95(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp97(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp96(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp98(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp97(fn, __VA_ARGS__))
|
||||
#define __WI_FOR_imp99(fn, arg, ...) fn(arg) WI_FLATTEN(__WI_FOR_imp98(fn, __VA_ARGS__))
|
||||
|
||||
#define __WI_FOR_imp(n, fnAndArgs) WI_PASTE(__WI_FOR_imp, n) fnAndArgs
|
||||
/// @endcond
|
||||
|
||||
//! Iterates through each of the given arguments invoking the specified macro against each one.
|
||||
#define WI_FOREACH(fn, ...) __WI_FOR_imp(WI_ARGS_COUNT(__VA_ARGS__), (fn, ##__VA_ARGS__))
|
||||
|
||||
//! Dispatches a single macro name to separate macros based on the number of arguments passed to it.
|
||||
#define WI_MACRO_DISPATCH(name, ...) WI_PASTE(WI_PASTE(name, WI_ARGS_COUNT(__VA_ARGS__)), (__VA_ARGS__))
|
||||
|
||||
//! @} // Macro composition helpers
|
||||
|
||||
#define __R_ENABLE_IF_IS_CLASS(ptrType) wistd::enable_if_t<wistd::is_class<ptrType>::value, void*> = (void*)0
|
||||
#define __R_ENABLE_IF_IS_NOT_CLASS(ptrType) wistd::enable_if_t<!wistd::is_class<ptrType>::value, void*> = (void*)0
|
||||
|
||||
//! @defgroup bitwise Bitwise Inspection and Manipulation
|
||||
//! Bitwise helpers to improve readability and reduce the error rate of bitwise operations.
|
||||
//! Several macros have been constructed to assist with bitwise inspection and manipulation. These macros exist
|
||||
//! for two primary purposes:
|
||||
//!
|
||||
//! 1. To improve the readability of bitwise comparisons and manipulation.
|
||||
//!
|
||||
//! The macro names are the more concise, readable form of what's being done and do not require that any flags
|
||||
//! or variables be specified multiple times for the comparisons.
|
||||
//!
|
||||
//! 2. To reduce the error rate associated with bitwise operations.
|
||||
//!
|
||||
//! The readability improvements naturally lend themselves to this by cutting down the number of concepts.
|
||||
//! Using `WI_IsFlagSet(var, MyEnum::Flag)` rather than `((var & MyEnum::Flag) == MyEnum::Flag)` removes the comparison
|
||||
//! operator and repetition in the flag value.
|
||||
//!
|
||||
//! Additionally, these macros separate single flag operations (which tend to be the most common) from multi-flag
|
||||
//! operations so that compile-time errors are generated for bitwise operations which are likely incorrect,
|
||||
//! such as: `WI_IsFlagSet(var, MyEnum::None)` or `WI_IsFlagSet(var, MyEnum::ValidMask)`.
|
||||
//!
|
||||
//! Note that the single flag helpers should be used when a compile-time constant single flag is being manipulated. These
|
||||
//! helpers provide compile-time errors on misuse and should be preferred over the multi-flag helpers. The multi-flag helpers
|
||||
//! should be used when multiple flags are being used simultaneously or when the flag values are not compile-time constants.
|
||||
//!
|
||||
//! Common example usage (manipulation of flag variables):
|
||||
//! ~~~~
|
||||
//! WI_SetFlag(m_flags, MyFlags::Foo); // Set a single flag in the given variable
|
||||
//! WI_SetAllFlags(m_flags, MyFlags::Foo | MyFlags::Bar); // Set one or more flags
|
||||
//! WI_ClearFlagIf(m_flags, MyFlags::Bar, isBarClosed); // Conditionally clear a single flag based upon a bool
|
||||
//! WI_ClearAllFlags(m_flags, MyFlags::Foo | MyFlags::Bar); // Clear one or more flags from the given variable
|
||||
//! WI_ToggleFlag(m_flags, MyFlags::Foo); // Toggle (change to the opposite value) a single flag
|
||||
//! WI_UpdateFlag(m_flags, MyFlags::Bar, isBarClosed); // Sets or Clears a single flag from the given variable based upon a bool value
|
||||
//! WI_UpdateFlagsInMask(m_flags, flagsMask, newFlagValues); // Sets or Clears the flags in flagsMask to the masked values from newFlagValues
|
||||
//! ~~~~
|
||||
//! Common example usage (inspection of flag variables):
|
||||
//! ~~~~
|
||||
//! if (WI_IsFlagSet(m_flags, MyFlags::Foo)) // Is a single flag set in the given variable?
|
||||
//! if (WI_IsAnyFlagSet(m_flags, MyFlags::Foo | MyFlags::Bar)) // Is at least one flag from the given mask set?
|
||||
//! if (WI_AreAllFlagsClear(m_flags, MyFlags::Foo | MyFlags::Bar)) // Are all flags in the given list clear?
|
||||
//! if (WI_IsSingleFlagSet(m_flags)) // Is *exactly* one flag set in the given variable?
|
||||
//! ~~~~
|
||||
//! @{
|
||||
|
||||
//! Returns the unsigned type of the same width and numeric value as the given enum
|
||||
#define WI_EnumValue(val) static_cast<::wil::integral_from_enum<decltype(val)>>(val)
|
||||
//! Validates that exactly ONE bit is set in compile-time constant `flag`
|
||||
#define WI_StaticAssertSingleBitSet(flag) static_cast<decltype(flag)>(::wil::details::verify_single_flag_helper<static_cast<unsigned long long>(WI_EnumValue(flag))>::value)
|
||||
|
||||
//! @name Bitwise manipulation macros
|
||||
//! @{
|
||||
|
||||
//! Set zero or more bitflags specified by `flags` in the variable `var`.
|
||||
#define WI_SetAllFlags(var, flags) ((var) |= (flags))
|
||||
//! Set a single compile-time constant `flag` in the variable `var`.
|
||||
#define WI_SetFlag(var, flag) WI_SetAllFlags(var, WI_StaticAssertSingleBitSet(flag))
|
||||
//! Conditionally sets a single compile-time constant `flag` in the variable `var` only if `condition` is true.
|
||||
#define WI_SetFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_SetFlag(var, flag); } } while ((void)0, 0)
|
||||
|
||||
//! Clear zero or more bitflags specified by `flags` from the variable `var`.
|
||||
#define WI_ClearAllFlags(var, flags) ((var) &= ~(flags))
|
||||
//! Clear a single compile-time constant `flag` from the variable `var`.
|
||||
#define WI_ClearFlag(var, flag) WI_ClearAllFlags(var, WI_StaticAssertSingleBitSet(flag))
|
||||
//! Conditionally clear a single compile-time constant `flag` in the variable `var` only if `condition` is true.
|
||||
#define WI_ClearFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_ClearFlag(var, flag); } } while ((void)0, 0)
|
||||
|
||||
//! Changes a single compile-time constant `flag` in the variable `var` to be set if `isFlagSet` is true or cleared if `isFlagSet` is false.
|
||||
#define WI_UpdateFlag(var, flag, isFlagSet) (wil::verify_bool(isFlagSet) ? WI_SetFlag(var, flag) : WI_ClearFlag(var, flag))
|
||||
//! Changes only the flags specified by `flagsMask` in the variable `var` to match the corresponding flags in `newFlags`.
|
||||
#define WI_UpdateFlagsInMask(var, flagsMask, newFlags) wil::details::UpdateFlagsInMaskHelper(var, flagsMask, newFlags)
|
||||
|
||||
//! Toggles (XOR the value) of multiple bitflags specified by `flags` in the variable `var`.
|
||||
#define WI_ToggleAllFlags(var, flags) ((var) ^= (flags))
|
||||
//! Toggles (XOR the value) of a single compile-time constant `flag` in the variable `var`.
|
||||
#define WI_ToggleFlag(var, flag) WI_ToggleAllFlags(var, WI_StaticAssertSingleBitSet(flag))
|
||||
//! @} // bitwise manipulation macros
|
||||
|
||||
//! @name Bitwise inspection macros
|
||||
//! @{
|
||||
|
||||
//! Evaluates as true if every bitflag specified in `flags` is set within `val`.
|
||||
#define WI_AreAllFlagsSet(val, flags) wil::details::AreAllFlagsSetHelper(val, flags)
|
||||
//! Evaluates as true if one or more bitflags specified in `flags` are set within `val`.
|
||||
#define WI_IsAnyFlagSet(val, flags) (static_cast<decltype((val) & (flags))>(WI_EnumValue(val) & WI_EnumValue(flags)) != static_cast<decltype((val) & (flags))>(0))
|
||||
//! Evaluates as true if a single compile-time constant `flag` is set within `val`.
|
||||
#define WI_IsFlagSet(val, flag) WI_IsAnyFlagSet(val, WI_StaticAssertSingleBitSet(flag))
|
||||
|
||||
//! Evaluates as true if every bitflag specified in `flags` is clear within `val`.
|
||||
#define WI_AreAllFlagsClear(val, flags) (static_cast<decltype((val) & (flags))>(WI_EnumValue(val) & WI_EnumValue(flags)) == static_cast<decltype((val) & (flags))>(0))
|
||||
//! Evaluates as true if one or more bitflags specified in `flags` are clear within `val`.
|
||||
#define WI_IsAnyFlagClear(val, flags) (!wil::details::AreAllFlagsSetHelper(val, flags))
|
||||
//! Evaluates as true if a single compile-time constant `flag` is clear within `val`.
|
||||
#define WI_IsFlagClear(val, flag) WI_AreAllFlagsClear(val, WI_StaticAssertSingleBitSet(flag))
|
||||
|
||||
//! Evaluates as true if exactly one bit (any bit) is set within `val`.
|
||||
#define WI_IsSingleFlagSet(val) wil::details::IsSingleFlagSetHelper(val)
|
||||
//! Evaluates as true if exactly one bit from within the specified `mask` is set within `val`.
|
||||
#define WI_IsSingleFlagSetInMask(val, mask) wil::details::IsSingleFlagSetHelper((val) & (mask))
|
||||
//! Evaluates as true if exactly one bit (any bit) is set within `val` or if there are no bits set within `val`.
|
||||
#define WI_IsClearOrSingleFlagSet(val) wil::details::IsClearOrSingleFlagSetHelper(val)
|
||||
//! Evaluates as true if exactly one bit from within the specified `mask` is set within `val` or if there are no bits from `mask` set within `val`.
|
||||
#define WI_IsClearOrSingleFlagSetInMask(val, mask) wil::details::IsClearOrSingleFlagSetHelper((val) & (mask))
|
||||
//! @}
|
||||
|
||||
#if defined(WIL_DOXYGEN)
|
||||
/** This macro provides a C++ header with a guaranteed initialization function.
|
||||
Normally, were a global object's constructor used for this purpose, the optimizer/linker might throw
|
||||
the object away if it's unreferenced (which throws away the side-effects that the initialization function
|
||||
was trying to achieve). Using this macro forces linker inclusion of a variable that's initialized by the
|
||||
provided function to elide that optimization.
|
||||
//!
|
||||
This functionality is primarily provided as a building block for header-based libraries (such as WIL)
|
||||
to be able to layer additional functionality into other libraries by their mere inclusion. Alternative models
|
||||
of initialization should be used whenever they are available.
|
||||
~~~~
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
WI_HEADER_INITITALIZATION_FUNCTION(InitializeDesktopFamilyApis, []
|
||||
{
|
||||
g_pfnGetModuleName = GetCurrentModuleName;
|
||||
g_pfnFailFastInLoaderCallout = FailFastInLoaderCallout;
|
||||
return 1;
|
||||
});
|
||||
#endif
|
||||
~~~~
|
||||
The above example is used within WIL to decide whether or not the library containing WIL is allowed to use
|
||||
desktop APIs. Building this functionality as #IFDEFs within functions would create ODR violations, whereas
|
||||
doing it with global function pointers and header initialization allows a runtime determination. */
|
||||
#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn)
|
||||
#elif defined(_M_IX86)
|
||||
#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) \
|
||||
extern "C" { __declspec(selectany) unsigned char g_header_init_ ## name = static_cast<unsigned char>(fn()); } \
|
||||
__pragma(comment(linker, "/INCLUDE:_g_header_init_" #name))
|
||||
#elif defined(_M_IA64) || defined(_M_AMD64) || defined(_M_ARM) || defined(_M_ARM64)
|
||||
#define WI_HEADER_INITITALIZATION_FUNCTION(name, fn) \
|
||||
extern "C" { __declspec(selectany) unsigned char g_header_init_ ## name = static_cast<unsigned char>(fn()); } \
|
||||
__pragma(comment(linker, "/INCLUDE:g_header_init_" #name))
|
||||
#else
|
||||
#error linker pragma must include g_header_init variation
|
||||
#endif
|
||||
|
||||
|
||||
/** All Windows Implementation Library classes and functions are located within the "wil" namespace.
|
||||
The 'wil' namespace is an intentionally short name as the intent is for code to be able to reference
|
||||
the namespace directly (example: `wil::srwlock lock;`) without a using statement. Resist adding a using
|
||||
statement for wil to avoid introducing potential name collisions between wil and other namespaces. */
|
||||
namespace wil
|
||||
{
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
template <typename T>
|
||||
class pointer_range
|
||||
{
|
||||
public:
|
||||
pointer_range(T begin_, T end_) : m_begin(begin_), m_end(end_) {}
|
||||
T begin() const { return m_begin; }
|
||||
T end() const { return m_end; }
|
||||
private:
|
||||
T m_begin;
|
||||
T m_end;
|
||||
};
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
/** Enables using range-based for between a begin and end object pointer.
|
||||
~~~~
|
||||
for (auto& obj : make_range(objPointerBegin, objPointerEnd)) { }
|
||||
~~~~ */
|
||||
template <typename T>
|
||||
details::pointer_range<T> make_range(T begin, T end)
|
||||
{
|
||||
return details::pointer_range<T>(begin, end);
|
||||
}
|
||||
|
||||
/** Enables using range-based for on a range when given the base pointer and the number of objects in the range.
|
||||
~~~~
|
||||
for (auto& obj : make_range(objPointer, objCount)) { }
|
||||
~~~~ */
|
||||
template <typename T>
|
||||
details::pointer_range<T> make_range(T begin, size_t count)
|
||||
{
|
||||
return details::pointer_range<T>(begin, begin + count);
|
||||
}
|
||||
|
||||
|
||||
//! @defgroup outparam Output Parameters
|
||||
//! Improve the conciseness of assigning values to optional output parameters.
|
||||
//! @{
|
||||
|
||||
/** Assign the given value to an optional output parameter.
|
||||
Makes code more concise by removing trivial `if (outParam)` blocks. */
|
||||
template <typename T>
|
||||
inline void assign_to_opt_param(_Out_opt_ T *outParam, T val)
|
||||
{
|
||||
if (outParam != nullptr)
|
||||
{
|
||||
*outParam = val;
|
||||
}
|
||||
}
|
||||
|
||||
/** Assign NULL to an optional output pointer parameter.
|
||||
Makes code more concise by removing trivial `if (outParam)` blocks. */
|
||||
template <typename T>
|
||||
inline void assign_null_to_opt_param(_Out_opt_ T *outParam)
|
||||
{
|
||||
if (outParam != nullptr)
|
||||
{
|
||||
*outParam = nullptr;
|
||||
}
|
||||
}
|
||||
//! @} // end output parameter helpers
|
||||
|
||||
/** Performs a logical or of the given variadic template parameters allowing indirect compile-time boolean evaluation.
|
||||
Example usage:
|
||||
~~~~
|
||||
template <unsigned int... Rest>
|
||||
struct FeatureRequiredBy
|
||||
{
|
||||
static const bool enabled = wil::variadic_logical_or<WilFeature<Rest>::enabled...>::value;
|
||||
};
|
||||
~~~~ */
|
||||
template <bool...> struct variadic_logical_or;
|
||||
/// @cond
|
||||
template <> struct variadic_logical_or<> : wistd::false_type { };
|
||||
template <bool... Rest> struct variadic_logical_or<true, Rest...> : wistd::true_type { };
|
||||
template <bool... Rest> struct variadic_logical_or<false, Rest...> : variadic_logical_or<Rest...>::type { };
|
||||
/// @endcond
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
template <unsigned long long flag>
|
||||
struct verify_single_flag_helper
|
||||
{
|
||||
static_assert((flag != 0) && ((flag & (flag - 1)) == 0), "Single flag expected, zero or multiple flags found");
|
||||
static const unsigned long long value = flag;
|
||||
};
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
|
||||
//! @defgroup typesafety Type Validation
|
||||
//! Helpers to validate variable types to prevent accidental, but allowed type conversions.
|
||||
//! These helpers are most useful when building macros that accept a particular type. Putting these functions around the types accepted
|
||||
//! prior to pushing that type through to a function (or using it within the macro) allows the macro to add an additional layer of type
|
||||
//! safety that would ordinarily be stripped away by C++ implicit conversions. This system is extensively used in the error handling helper
|
||||
//! macros to validate the types given to various macro parameters.
|
||||
//! @{
|
||||
|
||||
/** Verify that `val` can be evaluated as a logical bool.
|
||||
Other types will generate an intentional compilation error. Allowed types for a logical bool are bool, BOOL,
|
||||
boolean, BOOLEAN, and classes with an explicit bool cast.
|
||||
@param val The logical bool expression
|
||||
@return A C++ bool representing the evaluation of `val`. */
|
||||
template <typename T, __R_ENABLE_IF_IS_CLASS(T)>
|
||||
_Post_satisfies_(return == static_cast<bool>(val))
|
||||
__forceinline constexpr bool verify_bool(const T& val)
|
||||
{
|
||||
return static_cast<bool>(val);
|
||||
}
|
||||
|
||||
template <typename T, __R_ENABLE_IF_IS_NOT_CLASS(T)>
|
||||
__forceinline constexpr bool verify_bool(T /*val*/)
|
||||
{
|
||||
static_assert(!wistd::is_same<T, T>::value, "Wrong Type: bool/BOOL/BOOLEAN/boolean expected");
|
||||
return false;
|
||||
}
|
||||
|
||||
template <>
|
||||
_Post_satisfies_(return == val)
|
||||
__forceinline constexpr bool verify_bool<bool>(bool val)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
template <>
|
||||
_Post_satisfies_(return == (val != 0))
|
||||
__forceinline constexpr bool verify_bool<int>(int val)
|
||||
{
|
||||
return (val != 0);
|
||||
}
|
||||
|
||||
template <>
|
||||
_Post_satisfies_(return == !!val)
|
||||
__forceinline constexpr bool verify_bool<unsigned char>(unsigned char val)
|
||||
{
|
||||
return !!val;
|
||||
}
|
||||
|
||||
/** Verify that `val` is a Win32 BOOL value.
|
||||
Other types (including other logical bool expressions) will generate an intentional compilation error. Note that this will
|
||||
accept any `int` value as long as that is the underlying typedef behind `BOOL`.
|
||||
@param val The Win32 BOOL returning expression
|
||||
@return A Win32 BOOL representing the evaluation of `val`. */
|
||||
template <typename T>
|
||||
_Post_satisfies_(return == val)
|
||||
__forceinline constexpr int verify_BOOL(T val)
|
||||
{
|
||||
// Note: Written in terms of 'int' as BOOL is actually: typedef int BOOL;
|
||||
static_assert((wistd::is_same<T, int>::value), "Wrong Type: BOOL expected");
|
||||
return val;
|
||||
}
|
||||
|
||||
/** Verify that `hr` is an HRESULT value.
|
||||
Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is the
|
||||
underlying typedef behind HRESULT.
|
||||
//!
|
||||
Note that occasionally you might run into an HRESULT which is directly defined with a #define, such as:
|
||||
~~~~
|
||||
#define UIA_E_NOTSUPPORTED 0x80040204
|
||||
~~~~
|
||||
Though this looks like an `HRESULT`, this is actually an `unsigned long` (the hex specification forces this). When
|
||||
these are encountered and they are NOT in the public SDK (have not yet shipped to the public), then you should change
|
||||
their definition to match the manner in which `HRESULT` constants are defined in winerror.h:
|
||||
~~~~
|
||||
#define E_NOTIMPL _HRESULT_TYPEDEF_(0x80004001L)
|
||||
~~~~
|
||||
When these are encountered in the public SDK, their type should not be changed and you should use a static_cast
|
||||
to use this value in a macro that utilizes `verify_hresult`, for example:
|
||||
~~~~
|
||||
RETURN_HR_IF(static_cast<HRESULT>(UIA_E_NOTSUPPORTED), (patternId != UIA_DragPatternId));
|
||||
~~~~
|
||||
@param val The HRESULT returning expression
|
||||
@return An HRESULT representing the evaluation of `val`. */
|
||||
template <typename T>
|
||||
_Post_satisfies_(return == hr)
|
||||
inline constexpr long verify_hresult(T hr)
|
||||
{
|
||||
// Note: Written in terms of 'int' as HRESULT is actually: typedef _Return_type_success_(return >= 0) long HRESULT
|
||||
static_assert(wistd::is_same<T, long>::value, "Wrong Type: HRESULT expected");
|
||||
return hr;
|
||||
}
|
||||
/// @} // end type validation routines
|
||||
|
||||
/// @cond
|
||||
// Implementation details for macros and helper functions... do not use directly.
|
||||
namespace details
|
||||
{
|
||||
// Use size-specific casts to avoid sign extending numbers -- avoid warning C4310: cast truncates constant value
|
||||
#define __WI_MAKE_UNSIGNED(val) \
|
||||
(__pragma(warning(push)) __pragma(warning(disable: 4310 4309)) (sizeof(val) == 1 ? static_cast<unsigned char>(val) : \
|
||||
sizeof(val) == 2 ? static_cast<unsigned short>(val) : \
|
||||
sizeof(val) == 4 ? static_cast<unsigned long>(val) : \
|
||||
static_cast<unsigned long long>(val)) __pragma(warning(pop)))
|
||||
#define __WI_IS_UNSIGNED_SINGLE_FLAG_SET(val) ((val) && !((val) & ((val) - 1)))
|
||||
#define __WI_IS_SINGLE_FLAG_SET(val) __WI_IS_UNSIGNED_SINGLE_FLAG_SET(__WI_MAKE_UNSIGNED(val))
|
||||
|
||||
template <typename TVal, typename TFlags>
|
||||
__forceinline constexpr bool AreAllFlagsSetHelper(TVal val, TFlags flags)
|
||||
{
|
||||
return ((val & flags) == static_cast<decltype(val & flags)>(flags));
|
||||
}
|
||||
|
||||
template <typename TVal>
|
||||
__forceinline constexpr bool IsSingleFlagSetHelper(TVal val)
|
||||
{
|
||||
return __WI_IS_SINGLE_FLAG_SET(val);
|
||||
}
|
||||
|
||||
template <typename TVal>
|
||||
__forceinline constexpr bool IsClearOrSingleFlagSetHelper(TVal val)
|
||||
{
|
||||
return ((val == static_cast<wistd::remove_reference_t<TVal>>(0)) || IsSingleFlagSetHelper(val));
|
||||
}
|
||||
|
||||
template <typename TVal, typename TMask, typename TFlags>
|
||||
__forceinline constexpr void UpdateFlagsInMaskHelper(_Inout_ TVal& val, TMask mask, TFlags flags)
|
||||
{
|
||||
val = static_cast<wistd::remove_reference_t<TVal>>((val & ~mask) | (flags & mask));
|
||||
}
|
||||
|
||||
template <long>
|
||||
struct variable_size;
|
||||
|
||||
template <>
|
||||
struct variable_size<1>
|
||||
{
|
||||
typedef unsigned char type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct variable_size<2>
|
||||
{
|
||||
typedef unsigned short type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct variable_size<4>
|
||||
{
|
||||
typedef unsigned long type;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct variable_size<8>
|
||||
{
|
||||
typedef unsigned long long type;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct variable_size_mapping
|
||||
{
|
||||
typedef typename variable_size<sizeof(T)>::type type;
|
||||
};
|
||||
} // details
|
||||
/// @endcond
|
||||
|
||||
/** Defines the unsigned type of the same width (1, 2, 4, or 8 bytes) as the given type.
|
||||
This allows code to generically convert any enum class to it's corresponding underlying type. */
|
||||
template <typename T>
|
||||
using integral_from_enum = typename details::variable_size_mapping<T>::type;
|
||||
} // wil
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
#endif // __cplusplus
|
||||
#endif // __WIL_COMMON_INCLUDED
|
251
Externals/WIL/include/wil/cppwinrt.h
vendored
Normal file
251
Externals/WIL/include/wil/cppwinrt.h
vendored
Normal file
@ -0,0 +1,251 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#ifndef __WIL_CPPWINRT_INCLUDED
|
||||
#define __WIL_CPPWINRT_INCLUDED
|
||||
|
||||
#include "common.h"
|
||||
#include <windows.h>
|
||||
#include <unknwn.h>
|
||||
#include <hstring.h>
|
||||
|
||||
// WIL and C++/WinRT use two different exception types for communicating HRESULT failures. Thus, both libraries need to
|
||||
// understand how to translate these exception types into the correct HRESULT values at the ABI boundary. Prior to
|
||||
// C++/WinRT "2.0" this was accomplished by injecting the WINRT_EXTERNAL_CATCH_CLAUSE macro - that WIL defines below -
|
||||
// into its exception handler (winrt::to_hresult). Starting with C++/WinRT "2.0" this mechanism has shifted to a global
|
||||
// function pointer - winrt_to_hresult_handler - that WIL sets automatically when this header is included and
|
||||
// 'CPPWINRT_SUPPRESS_STATIC_INITIALIZERS' is not defined.
|
||||
|
||||
/// @cond
|
||||
namespace wil::details
|
||||
{
|
||||
// Since the C++/WinRT version macro is a string...
|
||||
inline constexpr int major_version_from_string(const char* versionString)
|
||||
{
|
||||
int result = 0;
|
||||
auto str = versionString;
|
||||
while ((*str >= '0') && (*str <= '9'))
|
||||
{
|
||||
result = result * 10 + (*str - '0');
|
||||
++str;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
#ifdef CPPWINRT_VERSION
|
||||
// Prior to C++/WinRT "2.0" this header needed to be included before 'winrt/base.h' so that our definition of
|
||||
// 'WINRT_EXTERNAL_CATCH_CLAUSE' would get picked up in the implementation of 'winrt::to_hresult'. This is no longer
|
||||
// problematic, so only emit an error when using a version of C++/WinRT prior to 2.0
|
||||
static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2,
|
||||
"Please include wil/cppwinrt.h before including any C++/WinRT headers");
|
||||
#endif
|
||||
|
||||
// NOTE: Will eventually be removed once C++/WinRT 2.0 use can be assumed
|
||||
#ifdef WINRT_EXTERNAL_CATCH_CLAUSE
|
||||
#define __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE 1
|
||||
#else
|
||||
#define WINRT_EXTERNAL_CATCH_CLAUSE \
|
||||
catch (const wil::ResultException& e) \
|
||||
{ \
|
||||
return winrt::hresult_error(e.GetErrorCode(), winrt::to_hstring(e.what())).to_abi(); \
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "result_macros.h"
|
||||
#include <winrt/base.h>
|
||||
|
||||
#if __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE
|
||||
static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2,
|
||||
"C++/WinRT external catch clause already defined outside of WIL");
|
||||
#endif
|
||||
|
||||
// In C++/WinRT 2.0 and beyond, this function pointer exists. In earlier versions it does not. It's much easier to avoid
|
||||
// linker errors than it is to SFINAE on variable existence, so we declare the variable here, but are careful not to
|
||||
// use it unless the version of C++/WinRT is high enough
|
||||
extern std::int32_t(__stdcall* winrt_to_hresult_handler)(void*) noexcept;
|
||||
|
||||
/// @cond
|
||||
namespace wil::details
|
||||
{
|
||||
inline void MaybeGetExceptionString(
|
||||
const winrt::hresult_error& exception,
|
||||
_Out_writes_opt_(debugStringChars) PWSTR debugString,
|
||||
_When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars)
|
||||
{
|
||||
if (debugString)
|
||||
{
|
||||
StringCchPrintfW(debugString, debugStringChars, L"winrt::hresult_error: %ls", exception.message().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
inline HRESULT __stdcall ResultFromCaughtException_CppWinRt(
|
||||
_Inout_updates_opt_(debugStringChars) PWSTR debugString,
|
||||
_When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars,
|
||||
_Inout_ bool* isNormalized) noexcept
|
||||
{
|
||||
if (g_pfnResultFromCaughtException)
|
||||
{
|
||||
try
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (const ResultException& exception)
|
||||
{
|
||||
*isNormalized = true;
|
||||
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||
return exception.GetErrorCode();
|
||||
}
|
||||
catch (const winrt::hresult_error& exception)
|
||||
{
|
||||
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||
return exception.code().value;
|
||||
}
|
||||
catch (const std::bad_alloc& exception)
|
||||
{
|
||||
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
catch (const std::out_of_range& exception)
|
||||
{
|
||||
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||
return E_BOUNDS;
|
||||
}
|
||||
catch (const std::invalid_argument& exception)
|
||||
{
|
||||
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (const ResultException& exception)
|
||||
{
|
||||
*isNormalized = true;
|
||||
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||
return exception.GetErrorCode();
|
||||
}
|
||||
catch (const winrt::hresult_error& exception)
|
||||
{
|
||||
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||
return exception.code().value;
|
||||
}
|
||||
catch (const std::bad_alloc& exception)
|
||||
{
|
||||
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
catch (const std::out_of_range& exception)
|
||||
{
|
||||
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||
return E_BOUNDS;
|
||||
}
|
||||
catch (const std::invalid_argument& exception)
|
||||
{
|
||||
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
catch (const std::exception& exception)
|
||||
{
|
||||
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||
return HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// Fall through to returning 'S_OK' below
|
||||
}
|
||||
}
|
||||
|
||||
// Tell the caller that we were unable to map the exception by succeeding...
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
namespace wil
|
||||
{
|
||||
inline std::int32_t __stdcall winrt_to_hresult(void* returnAddress) noexcept
|
||||
{
|
||||
// C++/WinRT only gives us the return address (caller), so pass along an empty 'DiagnosticsInfo' since we don't
|
||||
// have accurate file/line/etc. information
|
||||
return static_cast<std::int32_t>(details::ReportFailure_CaughtException<FailureType::Return>(__R_DIAGNOSTICS_RA(DiagnosticsInfo{}, returnAddress)));
|
||||
}
|
||||
|
||||
inline void WilInitialize_CppWinRT()
|
||||
{
|
||||
details::g_pfnResultFromCaughtException_CppWinRt = details::ResultFromCaughtException_CppWinRt;
|
||||
if constexpr (details::major_version_from_string(CPPWINRT_VERSION) >= 2)
|
||||
{
|
||||
WI_ASSERT(winrt_to_hresult_handler == nullptr);
|
||||
winrt_to_hresult_handler = winrt_to_hresult;
|
||||
}
|
||||
}
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
#ifndef CPPWINRT_SUPPRESS_STATIC_INITIALIZERS
|
||||
WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "0")
|
||||
WI_HEADER_INITITALIZATION_FUNCTION(WilInitialize_CppWinRT, []
|
||||
{
|
||||
::wil::WilInitialize_CppWinRT();
|
||||
return 1;
|
||||
});
|
||||
#else
|
||||
WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "1")
|
||||
#endif
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
// Provides an overload of verify_hresult so that the WIL macros can recognize winrt::hresult as a valid "hresult" type.
|
||||
inline long verify_hresult(winrt::hresult hr) noexcept
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
|
||||
// Provides versions of get_abi and put_abi for genericity that directly use HSTRING for convenience.
|
||||
template <typename T>
|
||||
auto get_abi(T const& object) noexcept
|
||||
{
|
||||
return winrt::get_abi(object);
|
||||
}
|
||||
|
||||
inline auto get_abi(winrt::hstring const& object) noexcept
|
||||
{
|
||||
return static_cast<HSTRING>(winrt::get_abi(object));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto put_abi(T& object) noexcept
|
||||
{
|
||||
return winrt::put_abi(object);
|
||||
}
|
||||
|
||||
inline auto put_abi(winrt::hstring& object) noexcept
|
||||
{
|
||||
return reinterpret_cast<HSTRING*>(winrt::put_abi(object));
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __WIL_CPPWINRT_INCLUDED
|
908
Externals/WIL/include/wil/filesystem.h
vendored
Normal file
908
Externals/WIL/include/wil/filesystem.h
vendored
Normal file
@ -0,0 +1,908 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#ifndef __WIL_FILESYSTEM_INCLUDED
|
||||
#define __WIL_FILESYSTEM_INCLUDED
|
||||
|
||||
#ifdef _KERNEL_MODE
|
||||
#error This header is not supported in kernel-mode.
|
||||
#endif
|
||||
|
||||
#include <new>
|
||||
#include <combaseapi.h> // Needed for CoTaskMemFree() used in output of some helpers.
|
||||
#include <winbase.h> // LocalAlloc
|
||||
#include <PathCch.h>
|
||||
#include "result.h"
|
||||
#include "win32_helpers.h"
|
||||
#include "resource.h"
|
||||
|
||||
namespace wil
|
||||
{
|
||||
//! Determines if a path is an extended length path that can be used to access paths longer than MAX_PATH.
|
||||
inline bool is_extended_length_path(_In_ PCWSTR path)
|
||||
{
|
||||
return wcsncmp(path, L"\\\\?\\", 4) == 0;
|
||||
}
|
||||
|
||||
//! Find the last segment of a path. Matches the behavior of shlwapi!PathFindFileNameW()
|
||||
//! note, does not support streams being specified like PathFindFileNameW(), is that a bug or a feature?
|
||||
inline PCWSTR find_last_path_segment(_In_ PCWSTR path)
|
||||
{
|
||||
auto const pathLength = wcslen(path);
|
||||
// If there is a trailing slash ignore that in the search.
|
||||
auto const limitedLength = ((pathLength > 0) && (path[pathLength - 1] == L'\\')) ? (pathLength - 1) : pathLength;
|
||||
|
||||
PCWSTR result;
|
||||
auto const offset = FindStringOrdinal(FIND_FROMEND, path, static_cast<int>(limitedLength), L"\\", 1, TRUE);
|
||||
if (offset == -1)
|
||||
{
|
||||
result = path + pathLength; // null terminator
|
||||
}
|
||||
else
|
||||
{
|
||||
result = path + offset + 1; // just past the slash
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//! Determine if the file name is one of the special "." or ".." names.
|
||||
inline bool path_is_dot_or_dotdot(_In_ PCWSTR fileName)
|
||||
{
|
||||
return ((fileName[0] == L'.') &&
|
||||
((fileName[1] == L'\0') || ((fileName[1] == L'.') && (fileName[2] == L'\0'))));
|
||||
}
|
||||
|
||||
//! Returns the drive number, if it has one. Returns true if there is a drive number, false otherwise. Supports regular and extended length paths.
|
||||
inline bool try_get_drive_letter_number(_In_ PCWSTR path, _Out_ int* driveNumber)
|
||||
{
|
||||
if (path[0] == L'\\' && path[1] == L'\\' && path[2] == L'?' && path[3] == L'\\')
|
||||
{
|
||||
path += 4;
|
||||
}
|
||||
if (path[0] && (path[1] == L':'))
|
||||
{
|
||||
if ((path[0] >= L'a') && (path[0] <= L'z'))
|
||||
{
|
||||
*driveNumber = path[0] - L'a';
|
||||
return true;
|
||||
}
|
||||
else if ((path[0] >= L'A') && (path[0] <= L'Z'))
|
||||
{
|
||||
*driveNumber = path[0] - L'A';
|
||||
return true;
|
||||
}
|
||||
}
|
||||
*driveNumber = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
|
||||
// PathCch.h APIs are only in desktop API for now.
|
||||
|
||||
// Compute the substring in the input value that is the parent folder path.
|
||||
// returns:
|
||||
// true + parentPathLength - path has a parent starting at the beginning path and of parentPathLength length.
|
||||
// false, no parent path, the input is a root path.
|
||||
inline bool try_get_parent_path_range(_In_ PCWSTR path, _Out_ size_t* parentPathLength)
|
||||
{
|
||||
*parentPathLength = 0;
|
||||
bool hasParent = false;
|
||||
PCWSTR rootEnd;
|
||||
if (SUCCEEDED(PathCchSkipRoot(path, &rootEnd)) && (*rootEnd != L'\0'))
|
||||
{
|
||||
auto const lastSegment = find_last_path_segment(path);
|
||||
*parentPathLength = lastSegment - path;
|
||||
hasParent = (*parentPathLength != 0);
|
||||
}
|
||||
return hasParent;
|
||||
}
|
||||
|
||||
// Creates directories for the specified path, creating parent paths
|
||||
// as needed.
|
||||
inline HRESULT CreateDirectoryDeepNoThrow(PCWSTR path) WI_NOEXCEPT
|
||||
{
|
||||
if (::CreateDirectoryW(path, nullptr) == FALSE)
|
||||
{
|
||||
DWORD const lastError = ::GetLastError();
|
||||
if (lastError == ERROR_PATH_NOT_FOUND)
|
||||
{
|
||||
size_t parentLength;
|
||||
if (try_get_parent_path_range(path, &parentLength))
|
||||
{
|
||||
wistd::unique_ptr<wchar_t[]> parent(new (std::nothrow) wchar_t[parentLength + 1]);
|
||||
RETURN_IF_NULL_ALLOC(parent.get());
|
||||
RETURN_IF_FAILED(StringCchCopyNW(parent.get(), parentLength + 1, path, parentLength));
|
||||
CreateDirectoryDeepNoThrow(parent.get()); // recurs
|
||||
}
|
||||
RETURN_IF_WIN32_BOOL_FALSE(::CreateDirectoryW(path, nullptr));
|
||||
}
|
||||
else if (lastError != ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
RETURN_WIN32(lastError);
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
inline void CreateDirectoryDeep(PCWSTR path)
|
||||
{
|
||||
THROW_IF_FAILED(CreateDirectoryDeepNoThrow(path));
|
||||
}
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
//! A strongly typed version of the Win32 API GetFullPathNameW.
|
||||
//! Return a path in an allocated buffer for handling long paths.
|
||||
//! Optionally return the pointer to the file name part.
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT GetFullPathNameW(PCWSTR file, string_type& path, _Outptr_opt_ PCWSTR* filePart = nullptr)
|
||||
{
|
||||
wil::assign_null_to_opt_param(filePart);
|
||||
const auto hr = AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(path,
|
||||
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT
|
||||
{
|
||||
// Note that GetFullPathNameW() is not limited to MAX_PATH
|
||||
// but it does take a fixed size buffer.
|
||||
*valueLengthNeededWithNull = ::GetFullPathNameW(file, static_cast<DWORD>(valueLength), value, nullptr);
|
||||
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0);
|
||||
WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength));
|
||||
if (*valueLengthNeededWithNull < valueLength)
|
||||
{
|
||||
(*valueLengthNeededWithNull)++; // it fit, account for the null
|
||||
}
|
||||
return S_OK;
|
||||
});
|
||||
if (SUCCEEDED(hr) && filePart)
|
||||
{
|
||||
*filePart = wil::find_last_path_segment(details::string_maker<string_type>::get(path));
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
//! A strongly typed version of the Win32 API of GetFullPathNameW.
|
||||
//! Return a path in an allocated buffer for handling long paths.
|
||||
//! Optionally return the pointer to the file name part.
|
||||
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
|
||||
string_type GetFullPathNameW(PCWSTR file, _Outptr_opt_ PCWSTR* filePart = nullptr)
|
||||
{
|
||||
string_type result;
|
||||
THROW_IF_FAILED((GetFullPathNameW<string_type, stackBufferLength>(file, result, filePart)));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
enum class RemoveDirectoryOptions
|
||||
{
|
||||
None = 0,
|
||||
KeepRootDirectory = 0x1
|
||||
};
|
||||
DEFINE_ENUM_FLAG_OPERATORS(RemoveDirectoryOptions);
|
||||
|
||||
// If inputPath is a non-normalized name be sure to pass an extended length form to ensure
|
||||
// it can be addressed and deleted.
|
||||
inline HRESULT RemoveDirectoryRecursiveNoThrow(PCWSTR inputPath, RemoveDirectoryOptions options = RemoveDirectoryOptions::None) WI_NOEXCEPT
|
||||
{
|
||||
wil::unique_hlocal_string path;
|
||||
PATHCCH_OPTIONS combineOptions = PATHCCH_NONE;
|
||||
|
||||
if (is_extended_length_path(inputPath))
|
||||
{
|
||||
path = wil::make_hlocal_string_nothrow(inputPath);
|
||||
RETURN_IF_NULL_ALLOC(path);
|
||||
// PathAllocCombine will convert extended length paths to regular paths if shorter than
|
||||
// MAX_PATH, avoid that behavior to provide access inputPath with non-normalized names.
|
||||
combineOptions = PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For regular paths normalize here to get consistent results when searching and deleting.
|
||||
RETURN_IF_FAILED(wil::GetFullPathNameW(inputPath, path));
|
||||
combineOptions = PATHCCH_ALLOW_LONG_PATHS;
|
||||
}
|
||||
|
||||
wil::unique_hlocal_string searchPath;
|
||||
RETURN_IF_FAILED(::PathAllocCombine(path.get(), L"*", combineOptions, &searchPath));
|
||||
|
||||
WIN32_FIND_DATAW fd;
|
||||
wil::unique_hfind findHandle(::FindFirstFileW(searchPath.get(), &fd));
|
||||
RETURN_LAST_ERROR_IF(!findHandle);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
// skip "." and ".."
|
||||
if (!(WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY) && path_is_dot_or_dotdot(fd.cFileName)))
|
||||
{
|
||||
// Need to form an extended length path to provide the ability to delete paths > MAX_PATH
|
||||
// and files with non-normalized names (dots or spaces at the end).
|
||||
wil::unique_hlocal_string pathToDelete;
|
||||
RETURN_IF_FAILED(::PathAllocCombine(path.get(), fd.cFileName,
|
||||
PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH | PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, &pathToDelete));
|
||||
if (WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
RemoveDirectoryOptions localOptions = options;
|
||||
RETURN_IF_FAILED(RemoveDirectoryRecursiveNoThrow(pathToDelete.get(), WI_ClearFlag(localOptions, RemoveDirectoryOptions::KeepRootDirectory)));
|
||||
}
|
||||
else
|
||||
{
|
||||
// note: if pathToDelete is read-only this will fail, consider adding
|
||||
// RemoveDirectoryOptions::RemoveReadOnly to enable this behavior.
|
||||
RETURN_IF_WIN32_BOOL_FALSE(::DeleteFileW(pathToDelete.get()));
|
||||
}
|
||||
}
|
||||
|
||||
if (!::FindNextFileW(findHandle.get(), &fd))
|
||||
{
|
||||
auto const err = ::GetLastError();
|
||||
if (err == ERROR_NO_MORE_FILES)
|
||||
{
|
||||
break;
|
||||
}
|
||||
RETURN_WIN32(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (WI_IsFlagClear(options, RemoveDirectoryOptions::KeepRootDirectory))
|
||||
{
|
||||
RETURN_IF_WIN32_BOOL_FALSE(::RemoveDirectoryW(path.get()));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
inline void RemoveDirectoryRecursive(PCWSTR path, RemoveDirectoryOptions options = RemoveDirectoryOptions::None)
|
||||
{
|
||||
THROW_IF_FAILED(RemoveDirectoryRecursiveNoThrow(path, options));
|
||||
}
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
// Range based for that supports Win32 structures that use NextEntryOffset as the basis of traversing
|
||||
// a result buffer that contains data. This is used in the following FileIO calls:
|
||||
// FileStreamInfo, FILE_STREAM_INFO
|
||||
// FileIdBothDirectoryInfo, FILE_ID_BOTH_DIR_INFO
|
||||
// FileFullDirectoryInfo, FILE_FULL_DIR_INFO
|
||||
// FileIdExtdDirectoryInfo, FILE_ID_EXTD_DIR_INFO
|
||||
// ReadDirectoryChangesW, FILE_NOTIFY_INFORMATION
|
||||
|
||||
template <typename T>
|
||||
struct next_entry_offset_iterator
|
||||
{
|
||||
// Fulfill std::iterator_traits requirements
|
||||
using difference_type = ptrdiff_t;
|
||||
using value_type = T;
|
||||
using pointer = const T*;
|
||||
using reference = const T&;
|
||||
#ifdef _XUTILITY_
|
||||
using iterator_category = ::std::forward_iterator_tag;
|
||||
#endif
|
||||
|
||||
next_entry_offset_iterator(T *iterable = __nullptr) : current_(iterable) {}
|
||||
|
||||
// range based for requires operator!=, operator++ and operator* to do its work
|
||||
// on the type returned from begin() and end(), provide those here.
|
||||
bool operator!=(const next_entry_offset_iterator& other) const { return current_ != other.current_; }
|
||||
|
||||
next_entry_offset_iterator& operator++()
|
||||
{
|
||||
current_ = (current_->NextEntryOffset != 0) ?
|
||||
reinterpret_cast<T *>(reinterpret_cast<unsigned char*>(current_) + current_->NextEntryOffset) :
|
||||
__nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
next_entry_offset_iterator operator++(int)
|
||||
{
|
||||
auto copy = *this;
|
||||
++(*this);
|
||||
return copy;
|
||||
}
|
||||
|
||||
reference operator*() const WI_NOEXCEPT { return *current_; }
|
||||
pointer operator->() const WI_NOEXCEPT { return current_; }
|
||||
|
||||
next_entry_offset_iterator<T> begin() { return *this; }
|
||||
next_entry_offset_iterator<T> end() { return next_entry_offset_iterator<T>(); }
|
||||
|
||||
T* current_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
next_entry_offset_iterator<T> create_next_entry_offset_iterator(T* p)
|
||||
{
|
||||
return next_entry_offset_iterator<T>(p);
|
||||
}
|
||||
|
||||
#pragma region Folder Watcher
|
||||
// Example use in exception based code:
|
||||
// auto watcher = wil::make_folder_watcher(folder.Path().c_str(), true, wil::allChangeEvents, []()
|
||||
// {
|
||||
// // respond
|
||||
// });
|
||||
//
|
||||
// Example use in result code based code:
|
||||
// wil::unique_folder_watcher watcher;
|
||||
// THROW_IF_FAILED(watcher.create(folder, true, wil::allChangeEvents, []()
|
||||
// {
|
||||
// // respond
|
||||
// }));
|
||||
|
||||
enum class FolderChangeEvent : DWORD
|
||||
{
|
||||
ChangesLost = 0, // requies special handling, reset state as events were lost
|
||||
Added = FILE_ACTION_ADDED,
|
||||
Removed = FILE_ACTION_REMOVED,
|
||||
Modified = FILE_ACTION_MODIFIED,
|
||||
RenameOldName = FILE_ACTION_RENAMED_OLD_NAME,
|
||||
RenameNewName = FILE_ACTION_RENAMED_NEW_NAME,
|
||||
};
|
||||
|
||||
enum class FolderChangeEvents : DWORD
|
||||
{
|
||||
None = 0,
|
||||
FileName = FILE_NOTIFY_CHANGE_FILE_NAME,
|
||||
DirectoryName = FILE_NOTIFY_CHANGE_DIR_NAME,
|
||||
Attributes = FILE_NOTIFY_CHANGE_ATTRIBUTES,
|
||||
FileSize = FILE_NOTIFY_CHANGE_SIZE,
|
||||
LastWriteTime = FILE_NOTIFY_CHANGE_LAST_WRITE,
|
||||
Security = FILE_NOTIFY_CHANGE_SECURITY,
|
||||
All = FILE_NOTIFY_CHANGE_FILE_NAME |
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME |
|
||||
FILE_NOTIFY_CHANGE_ATTRIBUTES |
|
||||
FILE_NOTIFY_CHANGE_SIZE |
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE |
|
||||
FILE_NOTIFY_CHANGE_SECURITY
|
||||
};
|
||||
DEFINE_ENUM_FLAG_OPERATORS(FolderChangeEvents);
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
struct folder_watcher_state
|
||||
{
|
||||
folder_watcher_state(wistd::function<void()> &&callback) : m_callback(wistd::move(callback))
|
||||
{
|
||||
}
|
||||
wistd::function<void()> m_callback;
|
||||
// Order is important, need to close the thread pool wait before the change handle.
|
||||
unique_hfind_change m_findChangeHandle;
|
||||
unique_threadpool_wait m_threadPoolWait;
|
||||
};
|
||||
|
||||
inline void delete_folder_watcher_state(_In_opt_ folder_watcher_state *storage) { delete storage; }
|
||||
|
||||
typedef resource_policy<folder_watcher_state *, decltype(&details::delete_folder_watcher_state),
|
||||
details::delete_folder_watcher_state, details::pointer_access_none> folder_watcher_state_resource_policy;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
template <typename storage_t, typename err_policy = err_exception_policy>
|
||||
class folder_watcher_t : public storage_t
|
||||
{
|
||||
public:
|
||||
// forward all base class constructors...
|
||||
template <typename... args_t>
|
||||
explicit folder_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward<args_t>(args)...) {}
|
||||
|
||||
// HRESULT or void error handling...
|
||||
typedef typename err_policy::result result;
|
||||
|
||||
// Exception-based constructors
|
||||
folder_watcher_t(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback)
|
||||
{
|
||||
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
|
||||
create(folderToWatch, isRecursive, filter, wistd::move(callback));
|
||||
}
|
||||
|
||||
result create(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback)
|
||||
{
|
||||
return err_policy::HResult(create_common(folderToWatch, isRecursive, filter, wistd::move(callback)));
|
||||
}
|
||||
private:
|
||||
// Factored into a standalone function to support Clang which does not support conversion of stateless lambdas
|
||||
// to __stdcall
|
||||
static void __stdcall callback(PTP_CALLBACK_INSTANCE /*Instance*/, void *context, TP_WAIT *pThreadPoolWait, TP_WAIT_RESULT /*result*/)
|
||||
{
|
||||
auto watcherState = static_cast<details::folder_watcher_state *>(context);
|
||||
watcherState->m_callback();
|
||||
|
||||
// Rearm the wait. Should not fail with valid parameters.
|
||||
FindNextChangeNotification(watcherState->m_findChangeHandle.get());
|
||||
SetThreadpoolWait(pThreadPoolWait, watcherState->m_findChangeHandle.get(), __nullptr);
|
||||
}
|
||||
|
||||
// This function exists to avoid template expansion of this code based on err_policy.
|
||||
HRESULT create_common(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback)
|
||||
{
|
||||
wistd::unique_ptr<details::folder_watcher_state> watcherState(new(std::nothrow) details::folder_watcher_state(wistd::move(callback)));
|
||||
RETURN_IF_NULL_ALLOC(watcherState);
|
||||
|
||||
watcherState->m_findChangeHandle.reset(FindFirstChangeNotificationW(folderToWatch, isRecursive, static_cast<DWORD>(filter)));
|
||||
RETURN_LAST_ERROR_IF(!watcherState->m_findChangeHandle);
|
||||
|
||||
watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(&folder_watcher_t::callback, watcherState.get(), __nullptr));
|
||||
RETURN_LAST_ERROR_IF(!watcherState->m_threadPoolWait);
|
||||
this->reset(watcherState.release()); // no more failures after this, pass ownership
|
||||
SetThreadpoolWait(this->get()->m_threadPoolWait.get(), this->get()->m_findChangeHandle.get(), __nullptr);
|
||||
return S_OK;
|
||||
}
|
||||
};
|
||||
|
||||
typedef unique_any_t<folder_watcher_t<details::unique_storage<details::folder_watcher_state_resource_policy>, err_returncode_policy>> unique_folder_watcher_nothrow;
|
||||
|
||||
inline unique_folder_watcher_nothrow make_folder_watcher_nothrow(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback) WI_NOEXCEPT
|
||||
{
|
||||
unique_folder_watcher_nothrow watcher;
|
||||
watcher.create(folderToWatch, isRecursive, filter, wistd::move(callback));
|
||||
return watcher; // caller must test for success using if (watcher)
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
typedef unique_any_t<folder_watcher_t<details::unique_storage<details::folder_watcher_state_resource_policy>, err_exception_policy>> unique_folder_watcher;
|
||||
|
||||
inline unique_folder_watcher make_folder_watcher(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void()> &&callback)
|
||||
{
|
||||
return unique_folder_watcher(folderToWatch, isRecursive, filter, wistd::move(callback));
|
||||
}
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Folder Reader
|
||||
|
||||
// Example use for throwing:
|
||||
// auto reader = wil::make_folder_change_reader(folder.Path().c_str(), true, wil::FolderChangeEvents::All,
|
||||
// [](wil::FolderChangeEvent event, PCWSTR fileName)
|
||||
// {
|
||||
// switch (event)
|
||||
// {
|
||||
// case wil::FolderChangeEvent::ChangesLost: break;
|
||||
// case wil::FolderChangeEvent::Added: break;
|
||||
// case wil::FolderChangeEvent::Removed: break;
|
||||
// case wil::FolderChangeEvent::Modified: break;
|
||||
// case wil::FolderChangeEvent::RenamedOldName: break;
|
||||
// case wil::FolderChangeEvent::RenamedNewName: break;
|
||||
// });
|
||||
//
|
||||
// Example use for non throwing:
|
||||
// wil::unique_folder_change_reader_nothrow reader;
|
||||
// THROW_IF_FAILED(reader.create(folder, true, wil::FolderChangeEvents::All,
|
||||
// [](wil::FolderChangeEvent event, PCWSTR fileName)
|
||||
// {
|
||||
// // handle changes
|
||||
// }));
|
||||
//
|
||||
|
||||
// @cond
|
||||
namespace details
|
||||
{
|
||||
struct folder_change_reader_state
|
||||
{
|
||||
folder_change_reader_state(bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
|
||||
: m_callback(wistd::move(callback)), m_isRecursive(isRecursive), m_filter(filter)
|
||||
{
|
||||
}
|
||||
|
||||
~folder_change_reader_state()
|
||||
{
|
||||
if (m_tpIo != __nullptr)
|
||||
{
|
||||
TP_IO *tpIo = m_tpIo;
|
||||
|
||||
// Indicate to the callback function that this object is being torn
|
||||
// down.
|
||||
|
||||
{
|
||||
auto autoLock = m_cancelLock.lock_exclusive();
|
||||
m_tpIo = __nullptr;
|
||||
}
|
||||
|
||||
// Cancel IO to terminate the file system monitoring operation.
|
||||
|
||||
if (m_folderHandle)
|
||||
{
|
||||
CancelIoEx(m_folderHandle.get(), &m_overlapped);
|
||||
}
|
||||
|
||||
// Wait for callbacks to complete.
|
||||
//
|
||||
// N.B. This is a blocking call and must not be made within a
|
||||
// callback or within a lock which is taken inside the
|
||||
// callback.
|
||||
|
||||
WaitForThreadpoolIoCallbacks(tpIo, TRUE);
|
||||
CloseThreadpoolIo(tpIo);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT StartIo()
|
||||
{
|
||||
// Unfortunately we have to handle ref-counting of IOs on behalf of the
|
||||
// thread pool.
|
||||
StartThreadpoolIo(m_tpIo);
|
||||
HRESULT hr = ReadDirectoryChangesW(m_folderHandle.get(), m_readBuffer, sizeof(m_readBuffer),
|
||||
m_isRecursive, static_cast<DWORD>(m_filter), __nullptr, &m_overlapped, __nullptr) ?
|
||||
S_OK : HRESULT_FROM_WIN32(::GetLastError());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// This operation does not have the usual semantic of returning
|
||||
// ERROR_IO_PENDING.
|
||||
// WI_ASSERT(hr != HRESULT_FROM_WIN32(ERROR_IO_PENDING));
|
||||
|
||||
// If the operation failed for whatever reason, ensure the TP
|
||||
// ref counts are accurate.
|
||||
|
||||
CancelThreadpoolIo(m_tpIo);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
// void (wil::FolderChangeEvent event, PCWSTR fileName)
|
||||
wistd::function<void(FolderChangeEvent, PCWSTR)> m_callback;
|
||||
unique_handle m_folderHandle;
|
||||
BOOL m_isRecursive = FALSE;
|
||||
FolderChangeEvents m_filter = FolderChangeEvents::None;
|
||||
OVERLAPPED m_overlapped{};
|
||||
TP_IO *m_tpIo = __nullptr;
|
||||
srwlock m_cancelLock;
|
||||
char m_readBuffer[4096]; // Consider alternative buffer sizes. With 512 byte buffer i was not able to observe overflow.
|
||||
};
|
||||
|
||||
inline void delete_folder_change_reader_state(_In_opt_ folder_change_reader_state *storage) { delete storage; }
|
||||
|
||||
typedef resource_policy<folder_change_reader_state *, decltype(&details::delete_folder_change_reader_state),
|
||||
details::delete_folder_change_reader_state, details::pointer_access_none> folder_change_reader_state_resource_policy;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
template <typename storage_t, typename err_policy = err_exception_policy>
|
||||
class folder_change_reader_t : public storage_t
|
||||
{
|
||||
public:
|
||||
// forward all base class constructors...
|
||||
template <typename... args_t>
|
||||
explicit folder_change_reader_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward<args_t>(args)...) {}
|
||||
|
||||
// HRESULT or void error handling...
|
||||
typedef typename err_policy::result result;
|
||||
|
||||
// Exception-based constructors
|
||||
folder_change_reader_t(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
|
||||
{
|
||||
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
|
||||
create(folderToWatch, isRecursive, filter, wistd::move(callback));
|
||||
}
|
||||
|
||||
result create(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
|
||||
{
|
||||
return err_policy::HResult(create_common(folderToWatch, isRecursive, filter, wistd::move(callback)));
|
||||
}
|
||||
|
||||
wil::unique_hfile& folder_handle() { return this->get()->m_folderHandle; }
|
||||
|
||||
private:
|
||||
// Factored into a standalone function to support Clang which does not support conversion of stateless lambdas
|
||||
// to __stdcall
|
||||
static void __stdcall callback(PTP_CALLBACK_INSTANCE /* Instance */, void *context, void * /*overlapped*/,
|
||||
ULONG result, ULONG_PTR /* BytesTransferred */, TP_IO * /* Io */)
|
||||
{
|
||||
auto readerState = static_cast<details::folder_change_reader_state *>(context);
|
||||
// WI_ASSERT(overlapped == &readerState->m_overlapped);
|
||||
|
||||
bool requeue = true;
|
||||
if (result == ERROR_SUCCESS)
|
||||
{
|
||||
for (auto const& info : create_next_entry_offset_iterator(reinterpret_cast<FILE_NOTIFY_INFORMATION *>(readerState->m_readBuffer)))
|
||||
{
|
||||
wchar_t realtiveFileName[MAX_PATH];
|
||||
StringCchCopyNW(realtiveFileName, ARRAYSIZE(realtiveFileName), info.FileName, info.FileNameLength / sizeof(info.FileName[0]));
|
||||
|
||||
readerState->m_callback(static_cast<FolderChangeEvent>(info.Action), realtiveFileName);
|
||||
}
|
||||
}
|
||||
else if (result == ERROR_NOTIFY_ENUM_DIR)
|
||||
{
|
||||
readerState->m_callback(FolderChangeEvent::ChangesLost, __nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
requeue = false;
|
||||
}
|
||||
|
||||
if (requeue)
|
||||
{
|
||||
// If the lock is held non-shared or the TP IO is nullptr, this
|
||||
// structure is being torn down. Otherwise, monitor for further
|
||||
// changes.
|
||||
auto autoLock = readerState->m_cancelLock.try_lock_shared();
|
||||
if (autoLock && readerState->m_tpIo)
|
||||
{
|
||||
readerState->StartIo(); // ignoring failure here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function exists to avoid template expansion of this code based on err_policy.
|
||||
HRESULT create_common(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
|
||||
{
|
||||
wistd::unique_ptr<details::folder_change_reader_state> readerState(new(std::nothrow) details::folder_change_reader_state(
|
||||
isRecursive, filter, wistd::move(callback)));
|
||||
RETURN_IF_NULL_ALLOC(readerState);
|
||||
|
||||
readerState->m_folderHandle.reset(CreateFileW(folderToWatch,
|
||||
FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_DELETE | FILE_SHARE_WRITE,
|
||||
__nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, __nullptr));
|
||||
RETURN_LAST_ERROR_IF(!readerState->m_folderHandle);
|
||||
|
||||
readerState->m_tpIo = CreateThreadpoolIo(readerState->m_folderHandle.get(), &folder_change_reader_t::callback, readerState.get(), __nullptr);
|
||||
RETURN_LAST_ERROR_IF_NULL(readerState->m_tpIo);
|
||||
RETURN_IF_FAILED(readerState->StartIo());
|
||||
this->reset(readerState.release());
|
||||
return S_OK;
|
||||
}
|
||||
};
|
||||
|
||||
typedef unique_any_t<folder_change_reader_t<details::unique_storage<details::folder_change_reader_state_resource_policy>, err_returncode_policy>> unique_folder_change_reader_nothrow;
|
||||
|
||||
inline unique_folder_change_reader_nothrow make_folder_change_reader_nothrow(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter,
|
||||
wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback) WI_NOEXCEPT
|
||||
{
|
||||
unique_folder_change_reader_nothrow watcher;
|
||||
watcher.create(folderToWatch, isRecursive, filter, wistd::move(callback));
|
||||
return watcher; // caller must test for success using if (watcher)
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
typedef unique_any_t<folder_change_reader_t<details::unique_storage<details::folder_change_reader_state_resource_policy>, err_exception_policy>> unique_folder_change_reader;
|
||||
|
||||
inline unique_folder_change_reader make_folder_change_reader(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter,
|
||||
wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
|
||||
{
|
||||
return unique_folder_change_reader(folderToWatch, isRecursive, filter, wistd::move(callback));
|
||||
}
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
#pragma endregion
|
||||
|
||||
//! Dos and VolumeGuid paths are always extended length paths with the \\?\ prefix.
|
||||
enum class VolumePrefix
|
||||
{
|
||||
Dos = VOLUME_NAME_DOS, // Extended Dos Device path form, e.g. \\?\C:\Users\Chris\AppData\Local\Temp\wil8C31.tmp
|
||||
VolumeGuid = VOLUME_NAME_GUID, // \\?\Volume{588fb606-b95b-4eae-b3cb-1e49861aaf18}\Users\Chris\AppData\Local\Temp\wil8C31.tmp
|
||||
// The following are special paths which can't be used with Win32 APIs, but are useful in other scenarios.
|
||||
None = VOLUME_NAME_NONE, // Path without the volume root, e.g. \Users\Chris\AppData\Local\Temp\wil8C31.tmp
|
||||
NtObjectName = VOLUME_NAME_NT, // Unique name used by Object Manager, e.g. \Device\HarddiskVolume4\Users\Chris\AppData\Local\Temp\wil8C31.tmp
|
||||
};
|
||||
enum class PathOptions
|
||||
{
|
||||
Normalized = FILE_NAME_NORMALIZED,
|
||||
Opened = FILE_NAME_OPENED,
|
||||
};
|
||||
DEFINE_ENUM_FLAG_OPERATORS(PathOptions);
|
||||
|
||||
/** A strongly typed version of the Win32 API GetFinalPathNameByHandleW.
|
||||
Get the full path name in different forms
|
||||
Use this instead + VolumePrefix::None instead of GetFileInformationByHandleEx(FileNameInfo) to
|
||||
get that path form. */
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT GetFinalPathNameByHandleW(HANDLE fileHandle, string_type& path,
|
||||
wil::VolumePrefix volumePrefix = wil::VolumePrefix::Dos, wil::PathOptions options = wil::PathOptions::Normalized)
|
||||
{
|
||||
return AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(path,
|
||||
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT
|
||||
{
|
||||
*valueLengthNeededWithNull = ::GetFinalPathNameByHandleW(fileHandle, value, static_cast<DWORD>(valueLength),
|
||||
static_cast<DWORD>(volumePrefix) | static_cast<DWORD>(options));
|
||||
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0);
|
||||
WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength));
|
||||
if (*valueLengthNeededWithNull < valueLength)
|
||||
{
|
||||
(*valueLengthNeededWithNull)++; // it fit, account for the null
|
||||
}
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
/** A strongly typed version of the Win32 API GetFinalPathNameByHandleW.
|
||||
Get the full path name in different forms. Use this + VolumePrefix::None
|
||||
instead of GetFileInformationByHandleEx(FileNameInfo) to get that path form. */
|
||||
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
|
||||
string_type GetFinalPathNameByHandleW(HANDLE fileHandle,
|
||||
wil::VolumePrefix volumePrefix = wil::VolumePrefix::Dos, wil::PathOptions options = wil::PathOptions::Normalized)
|
||||
{
|
||||
string_type result;
|
||||
THROW_IF_FAILED((GetFinalPathNameByHandleW<string_type, stackBufferLength>(fileHandle, result, volumePrefix, options)));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
//! A strongly typed version of the Win32 API of GetCurrentDirectoryW.
|
||||
//! Return a path in an allocated buffer for handling long paths.
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT GetCurrentDirectoryW(string_type& path)
|
||||
{
|
||||
return AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(path,
|
||||
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNull) -> HRESULT
|
||||
{
|
||||
*valueLengthNeededWithNull = ::GetCurrentDirectoryW(static_cast<DWORD>(valueLength), value);
|
||||
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNull == 0);
|
||||
WI_ASSERT((*value != L'\0') == (*valueLengthNeededWithNull < valueLength));
|
||||
if (*valueLengthNeededWithNull < valueLength)
|
||||
{
|
||||
(*valueLengthNeededWithNull)++; // it fit, account for the null
|
||||
}
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
//! A strongly typed version of the Win32 API of GetCurrentDirectoryW.
|
||||
//! Return a path in an allocated buffer for handling long paths.
|
||||
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
|
||||
string_type GetCurrentDirectoryW()
|
||||
{
|
||||
string_type result;
|
||||
THROW_IF_FAILED((GetCurrentDirectoryW<string_type, stackBufferLength>(result)));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO: add support for these and other similar APIs.
|
||||
// GetShortPathNameW()
|
||||
// GetLongPathNameW()
|
||||
// GetWindowsDirectory()
|
||||
// GetTempDirectory()
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
template <FILE_INFO_BY_HANDLE_CLASS infoClass> struct MapInfoClassToInfoStruct; // failure to map is a usage error caught by the compiler
|
||||
#define MAP_INFOCLASS_TO_STRUCT(InfoClass, InfoStruct, IsFixed, Extra) \
|
||||
template <> struct MapInfoClassToInfoStruct<InfoClass> \
|
||||
{ \
|
||||
typedef InfoStruct type; \
|
||||
static bool const isFixed = IsFixed; \
|
||||
static size_t const extraSize = Extra; \
|
||||
};
|
||||
|
||||
MAP_INFOCLASS_TO_STRUCT(FileBasicInfo, FILE_BASIC_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileStandardInfo, FILE_STANDARD_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileNameInfo, FILE_NAME_INFO, false, 32);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileRenameInfo, FILE_RENAME_INFO, false, 32);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileDispositionInfo, FILE_DISPOSITION_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileAllocationInfo, FILE_ALLOCATION_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileEndOfFileInfo, FILE_END_OF_FILE_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileStreamInfo, FILE_STREAM_INFO, false, 32);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileCompressionInfo, FILE_COMPRESSION_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileAttributeTagInfo, FILE_ATTRIBUTE_TAG_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileIdBothDirectoryInfo, FILE_ID_BOTH_DIR_INFO, false, 4096);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileIdBothDirectoryRestartInfo, FILE_ID_BOTH_DIR_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileIoPriorityHintInfo, FILE_IO_PRIORITY_HINT_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileRemoteProtocolInfo, FILE_REMOTE_PROTOCOL_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileFullDirectoryInfo, FILE_FULL_DIR_INFO, false, 4096);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileFullDirectoryRestartInfo, FILE_FULL_DIR_INFO, true, 0);
|
||||
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||
MAP_INFOCLASS_TO_STRUCT(FileStorageInfo, FILE_STORAGE_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileAlignmentInfo, FILE_ALIGNMENT_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileIdInfo, FILE_ID_INFO, true, 0);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileIdExtdDirectoryInfo, FILE_ID_EXTD_DIR_INFO, false, 4096);
|
||||
MAP_INFOCLASS_TO_STRUCT(FileIdExtdDirectoryRestartInfo, FILE_ID_EXTD_DIR_INFO, true, 0);
|
||||
#endif
|
||||
|
||||
// Type unsafe version used in the implementation to avoid template bloat.
|
||||
inline HRESULT GetFileInfo(HANDLE fileHandle, FILE_INFO_BY_HANDLE_CLASS infoClass, size_t allocationSize,
|
||||
_Outptr_result_nullonfailure_ void **result)
|
||||
{
|
||||
*result = nullptr;
|
||||
|
||||
wistd::unique_ptr<char[]> resultHolder(new(std::nothrow) char[allocationSize]);
|
||||
RETURN_IF_NULL_ALLOC(resultHolder);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (GetFileInformationByHandleEx(fileHandle, infoClass, resultHolder.get(), static_cast<DWORD>(allocationSize)))
|
||||
{
|
||||
*result = resultHolder.release();
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
DWORD const lastError = ::GetLastError();
|
||||
if (lastError == ERROR_MORE_DATA)
|
||||
{
|
||||
allocationSize *= 2;
|
||||
resultHolder.reset(new(std::nothrow) char[allocationSize]);
|
||||
RETURN_IF_NULL_ALLOC(resultHolder);
|
||||
}
|
||||
else if (lastError == ERROR_NO_MORE_FILES) // for folder enumeration cases
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (lastError == ERROR_INVALID_PARAMETER) // operation not supported by file system
|
||||
{
|
||||
return HRESULT_FROM_WIN32(lastError);
|
||||
}
|
||||
else
|
||||
{
|
||||
RETURN_WIN32(lastError);
|
||||
}
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
/** Get file information for a variable sized structure, returns an HRESULT.
|
||||
~~~
|
||||
wistd::unique_ptr<FILE_NAME_INFO> fileNameInfo;
|
||||
RETURN_IF_FAILED(GetFileInfoNoThrow<FileNameInfo>(fileHandle, fileNameInfo));
|
||||
~~~
|
||||
*/
|
||||
template <FILE_INFO_BY_HANDLE_CLASS infoClass, typename wistd::enable_if<!details::MapInfoClassToInfoStruct<infoClass>::isFixed, int>::type = 0>
|
||||
HRESULT GetFileInfoNoThrow(HANDLE fileHandle, wistd::unique_ptr<typename details::MapInfoClassToInfoStruct<infoClass>::type> &result) WI_NOEXCEPT
|
||||
{
|
||||
void *rawResult;
|
||||
HRESULT hr = details::GetFileInfo(fileHandle, infoClass,
|
||||
sizeof(typename details::MapInfoClassToInfoStruct<infoClass>::type) + details::MapInfoClassToInfoStruct<infoClass>::extraSize,
|
||||
&rawResult);
|
||||
result.reset(static_cast<typename details::MapInfoClassToInfoStruct<infoClass>::type*>(rawResult));
|
||||
RETURN_HR_IF_EXPECTED(hr, hr == E_INVALIDARG); // operation not supported by file system
|
||||
RETURN_IF_FAILED(hr);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/** Get file information for a fixed sized structure, returns an HRESULT.
|
||||
~~~
|
||||
FILE_BASIC_INFO fileBasicInfo;
|
||||
RETURN_IF_FAILED(GetFileInfoNoThrow<FileBasicInfo>(fileHandle, &fileBasicInfo));
|
||||
~~~
|
||||
*/
|
||||
template <FILE_INFO_BY_HANDLE_CLASS infoClass, typename wistd::enable_if<details::MapInfoClassToInfoStruct<infoClass>::isFixed, int>::type = 0>
|
||||
HRESULT GetFileInfoNoThrow(HANDLE fileHandle, _Out_ typename details::MapInfoClassToInfoStruct<infoClass>::type *result) WI_NOEXCEPT
|
||||
{
|
||||
const HRESULT hr = GetFileInformationByHandleEx(fileHandle, infoClass, result, sizeof(*result)) ?
|
||||
S_OK : HRESULT_FROM_WIN32(::GetLastError());
|
||||
RETURN_HR_IF_EXPECTED(hr, hr == E_INVALIDARG); // operation not supported by file system
|
||||
RETURN_IF_FAILED(hr);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#ifdef _CPPUNWIND
|
||||
/** Get file information for a fixed sized structure, throws on failure.
|
||||
~~~
|
||||
auto fileBasicInfo = GetFileInfo<FileBasicInfo>(fileHandle);
|
||||
~~~
|
||||
*/
|
||||
template <FILE_INFO_BY_HANDLE_CLASS infoClass, typename wistd::enable_if<details::MapInfoClassToInfoStruct<infoClass>::isFixed, int>::type = 0>
|
||||
typename details::MapInfoClassToInfoStruct<infoClass>::type GetFileInfo(HANDLE fileHandle)
|
||||
{
|
||||
typename details::MapInfoClassToInfoStruct<infoClass>::type result;
|
||||
THROW_IF_FAILED(GetFileInfoNoThrow<infoClass>(fileHandle, &result));
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Get file information for a variable sized structure, throws on failure.
|
||||
~~~
|
||||
auto fileBasicInfo = GetFileInfo<FileNameInfo>(fileHandle);
|
||||
~~~
|
||||
*/
|
||||
template <FILE_INFO_BY_HANDLE_CLASS infoClass, typename wistd::enable_if<!details::MapInfoClassToInfoStruct<infoClass>::isFixed, int>::type = 0>
|
||||
wistd::unique_ptr<typename details::MapInfoClassToInfoStruct<infoClass>::type> GetFileInfo(HANDLE fileHandle)
|
||||
{
|
||||
wistd::unique_ptr<typename details::MapInfoClassToInfoStruct<infoClass>::type> result;
|
||||
THROW_IF_FAILED(GetFileInfoNoThrow<infoClass>(fileHandle, result));
|
||||
return result;
|
||||
}
|
||||
#endif // _CPPUNWIND
|
||||
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
}
|
||||
|
||||
#endif // __WIL_FILESYSTEM_INCLUDED
|
277
Externals/WIL/include/wil/registry.h
vendored
Normal file
277
Externals/WIL/include/wil/registry.h
vendored
Normal file
@ -0,0 +1,277 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#ifndef __WIL_REGISTRY_INCLUDED
|
||||
#define __WIL_REGISTRY_INCLUDED
|
||||
|
||||
#ifdef _KERNEL_MODE
|
||||
#error This header is not supported in kernel-mode.
|
||||
#endif
|
||||
|
||||
#include <winreg.h>
|
||||
#include <new.h> // new(std::nothrow)
|
||||
#include "resource.h" // unique_hkey
|
||||
|
||||
namespace wil
|
||||
{
|
||||
//! The key name includes the absolute path of the key in the registry, always starting at a
|
||||
//! base key, for example, HKEY_LOCAL_MACHINE.
|
||||
size_t const max_registry_key_name_length = 255;
|
||||
|
||||
//! The maximum number of characters allowed in a registry value's name.
|
||||
size_t const max_registry_value_name_length = 16383;
|
||||
|
||||
// unique_registry_watcher/unique_registry_watcher_nothrow/unique_registry_watcher_failfast
|
||||
// These classes make it easy to execute a provided function when a
|
||||
// registry key changes (optionally recursively). Specify the key
|
||||
// either as a root key + path, or an open registry handle as wil::unique_hkey
|
||||
// or a raw HKEY value (that will be duplicated).
|
||||
//
|
||||
// Example use with exceptions base error handling:
|
||||
// auto watcher = wil::make_registry_watcher(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind changeKind[]
|
||||
// {
|
||||
// if (changeKind == RegistryChangeKind::Delete)
|
||||
// {
|
||||
// watcher.reset();
|
||||
// }
|
||||
// // invalidate cached registry data here
|
||||
// });
|
||||
//
|
||||
// Example use with error code base error handling:
|
||||
// auto watcher = wil::make_registry_watcher_nothrow(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind[]
|
||||
// {
|
||||
// // invalidate cached registry data here
|
||||
// });
|
||||
// RETURN_IF_NULL_ALLOC(watcher);
|
||||
|
||||
enum class RegistryChangeKind
|
||||
{
|
||||
Modify = 0,
|
||||
Delete = 1,
|
||||
};
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
struct registry_watcher_state
|
||||
{
|
||||
registry_watcher_state(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||
: m_callback(wistd::move(callback)), m_keyToWatch(wistd::move(keyToWatch)), m_isRecursive(isRecursive)
|
||||
{
|
||||
}
|
||||
wistd::function<void(RegistryChangeKind)> m_callback;
|
||||
unique_hkey m_keyToWatch;
|
||||
unique_event_nothrow m_eventHandle;
|
||||
|
||||
// While not strictly needed since this is ref counted the thread pool wait
|
||||
// should be last to ensure that the other members are valid
|
||||
// when it is destructed as it will reference them.
|
||||
unique_threadpool_wait m_threadPoolWait;
|
||||
bool m_isRecursive;
|
||||
|
||||
volatile long m_refCount = 1;
|
||||
srwlock m_lock;
|
||||
|
||||
// Returns true if the refcount can be increased from a non zero value,
|
||||
// false it was zero impling that the object is in or on the way to the destructor.
|
||||
// In this case ReleaseFromCallback() should not be called.
|
||||
bool TryAddRef()
|
||||
{
|
||||
return ::InterlockedIncrement(&m_refCount) > 1;
|
||||
}
|
||||
|
||||
void Release()
|
||||
{
|
||||
auto lock = m_lock.lock_exclusive();
|
||||
if (0 == ::InterlockedDecrement(&m_refCount))
|
||||
{
|
||||
lock.reset(); // leave the lock before deleting it.
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void ReleaseFromCallback(bool rearm)
|
||||
{
|
||||
auto lock = m_lock.lock_exclusive();
|
||||
if (0 == ::InterlockedDecrement(&m_refCount))
|
||||
{
|
||||
// Destroy the thread pool wait now to avoid the wait that would occur in the
|
||||
// destructor. That wait would cause a deadlock since we are doing this from the callback.
|
||||
::CloseThreadpoolWait(m_threadPoolWait.release());
|
||||
lock.reset(); // leave the lock before deleting it.
|
||||
delete this;
|
||||
// Sleep(1); // Enable for testing to find use after free bugs.
|
||||
}
|
||||
else if (rearm)
|
||||
{
|
||||
::SetThreadpoolWait(m_threadPoolWait.get(), m_eventHandle.get(), nullptr);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
inline void delete_registry_watcher_state(_In_opt_ registry_watcher_state *watcherStorage) { watcherStorage->Release(); }
|
||||
|
||||
typedef resource_policy<registry_watcher_state *, decltype(&details::delete_registry_watcher_state),
|
||||
details::delete_registry_watcher_state, details::pointer_access_none> registry_watcher_state_resource_policy;
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
template <typename storage_t, typename err_policy = err_exception_policy>
|
||||
class registry_watcher_t : public storage_t
|
||||
{
|
||||
public:
|
||||
// forward all base class constructors...
|
||||
template <typename... args_t>
|
||||
explicit registry_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward<args_t>(args)...) {}
|
||||
|
||||
// HRESULT or void error handling...
|
||||
typedef typename err_policy::result result;
|
||||
|
||||
// Exception-based constructors
|
||||
registry_watcher_t(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||
{
|
||||
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
|
||||
create(rootKey, subKey, isRecursive, wistd::move(callback));
|
||||
}
|
||||
|
||||
registry_watcher_t(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||
{
|
||||
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
|
||||
create(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
|
||||
}
|
||||
|
||||
// Pass a root key, sub key pair or use an empty string to use rootKey as the key to watch.
|
||||
result create(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||
{
|
||||
// Most use will want to create the key, consider adding an option for open as a future design change.
|
||||
unique_hkey keyToWatch;
|
||||
HRESULT hr = HRESULT_FROM_WIN32(RegCreateKeyExW(rootKey, subKey, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToWatch, nullptr));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return err_policy::HResult(hr);
|
||||
}
|
||||
return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
|
||||
}
|
||||
|
||||
result create(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||
{
|
||||
return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
|
||||
}
|
||||
|
||||
private:
|
||||
// Factored into a standalone function to support Clang which does not support conversion of stateless lambdas
|
||||
// to __stdcall
|
||||
static void __stdcall callback(PTP_CALLBACK_INSTANCE, void *context, TP_WAIT *, TP_WAIT_RESULT)
|
||||
{
|
||||
#ifndef __WIL_REGISTRY_CHANGE_CALLBACK_TEST
|
||||
#define __WIL_REGISTRY_CHANGE_CALLBACK_TEST
|
||||
#endif
|
||||
__WIL_REGISTRY_CHANGE_CALLBACK_TEST
|
||||
auto watcherState = static_cast<details::registry_watcher_state *>(context);
|
||||
if (watcherState->TryAddRef())
|
||||
{
|
||||
// using auto reset event so don't need to manually reset.
|
||||
|
||||
// failure here is a programming error.
|
||||
const LSTATUS error = RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(), watcherState->m_isRecursive,
|
||||
REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC,
|
||||
watcherState->m_eventHandle.get(), TRUE);
|
||||
|
||||
// Call the client before re-arming to ensure that multiple callbacks don't
|
||||
// run concurrently.
|
||||
switch (error)
|
||||
{
|
||||
case ERROR_SUCCESS:
|
||||
case ERROR_ACCESS_DENIED:
|
||||
// Normal modification: send RegistryChangeKind::Modify and re-arm.
|
||||
watcherState->m_callback(RegistryChangeKind::Modify);
|
||||
watcherState->ReleaseFromCallback(true);
|
||||
break;
|
||||
|
||||
case ERROR_KEY_DELETED:
|
||||
// Key deleted, send RegistryChangeKind::Delete, do not re-arm.
|
||||
watcherState->m_callback(RegistryChangeKind::Delete);
|
||||
watcherState->ReleaseFromCallback(false);
|
||||
break;
|
||||
|
||||
case ERROR_HANDLE_REVOKED:
|
||||
// Handle revoked. This can occur if the user session ends before
|
||||
// the watcher shuts-down. Disarm silently since there is generally no way to respond.
|
||||
watcherState->ReleaseFromCallback(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
FAIL_FAST_HR(HRESULT_FROM_WIN32(error));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function exists to avoid template expansion of this code based on err_policy.
|
||||
HRESULT create_common(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||
{
|
||||
wistd::unique_ptr<details::registry_watcher_state> watcherState(new(std::nothrow) details::registry_watcher_state(
|
||||
wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
|
||||
RETURN_IF_NULL_ALLOC(watcherState);
|
||||
RETURN_IF_FAILED(watcherState->m_eventHandle.create());
|
||||
RETURN_IF_WIN32_ERROR(RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(),
|
||||
watcherState->m_isRecursive, REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC,
|
||||
watcherState->m_eventHandle.get(), TRUE));
|
||||
|
||||
watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(®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
|
5984
Externals/WIL/include/wil/resource.h
vendored
Normal file
5984
Externals/WIL/include/wil/resource.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1275
Externals/WIL/include/wil/result.h
vendored
Normal file
1275
Externals/WIL/include/wil/result.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5860
Externals/WIL/include/wil/result_macros.h
vendored
Normal file
5860
Externals/WIL/include/wil/result_macros.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
96
Externals/WIL/include/wil/result_originate.h
vendored
Normal file
96
Externals/WIL/include/wil/result_originate.h
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
|
||||
// Note: When origination is enabled by including this file, origination is done as part of the RETURN_* and THROW_* macros. Before originating
|
||||
// a new error we will observe whether there is already an error payload associated with the current thread. If there is, and the HRESULTs match,
|
||||
// then a new error will not be originated. Otherwise we will overwrite it with a new origination. The ABI boundary for WinRT APIs will check the
|
||||
// per-thread error information. The act of checking the error clears it, so there should be minimal risk of failing to originate distinct errors
|
||||
// simply because the HRESULTs match.
|
||||
//
|
||||
// For THROW_ macros we will examine the thread-local error storage once per throw. So typically once, with additional calls if the exception is
|
||||
// caught and re-thrown.
|
||||
//
|
||||
// For RETURN_ macros we will have to examine the thread-local error storage once per frame as the call stack unwinds. Because error conditions
|
||||
// -should- be uncommon the performance impact of checking TLS should be minimal. The more expensive part is originating the error because it must
|
||||
// capture the entire stack and some additional data.
|
||||
|
||||
#ifndef __WIL_RESULT_ORIGINATE_INCLUDED
|
||||
#define __WIL_RESULT_ORIGINATE_INCLUDED
|
||||
|
||||
#include "result.h"
|
||||
#include <OleAuto.h> // RestrictedErrorInfo uses BSTRs :(
|
||||
#include "resource.h"
|
||||
#include "com.h"
|
||||
#include <roerrorapi.h>
|
||||
|
||||
#ifndef __cplusplus_winrt // The CX runtime likes to originate errors already so we would conflict with them.
|
||||
namespace wil
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
// Note: The name must begin with "Raise" so that the !analyze auto-bucketing will ignore this stack frame. Otherwise this line of code gets all the blame.
|
||||
inline void __stdcall RaiseRoOriginateOnWilExceptions(wil::FailureInfo const& failure) WI_NOEXCEPT
|
||||
{
|
||||
if ((failure.type == FailureType::Return) || (failure.type == FailureType::Exception))
|
||||
{
|
||||
bool shouldOriginate = true;
|
||||
|
||||
wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
|
||||
if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK)
|
||||
{
|
||||
// This thread already has an error origination payload. Don't originate again if it has the same HRESULT that we are
|
||||
// observing right now.
|
||||
wil::unique_bstr descriptionUnused;
|
||||
HRESULT existingHr = failure.hr;
|
||||
wil::unique_bstr restrictedDescriptionUnused;
|
||||
wil::unique_bstr capabilitySidUnused;
|
||||
if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)))
|
||||
{
|
||||
shouldOriginate = (failure.hr != existingHr);
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldOriginate)
|
||||
{
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
|
||||
wil::unique_hmodule errorModule;
|
||||
if (GetModuleHandleExW(0, L"api-ms-win-core-winrt-error-l1-1-1.dll", &errorModule))
|
||||
{
|
||||
auto pfn = reinterpret_cast<decltype(&::RoOriginateError)>(GetProcAddress(errorModule.get(), "RoOriginateError"));
|
||||
if (pfn != nullptr)
|
||||
{
|
||||
pfn(failure.hr, nullptr);
|
||||
}
|
||||
}
|
||||
#else // DESKTOP | SYSTEM
|
||||
::RoOriginateError(failure.hr, nullptr);
|
||||
#endif // DESKTOP | SYSTEM
|
||||
}
|
||||
else if (restrictedErrorInformation)
|
||||
{
|
||||
// GetRestrictedErrorInfo returns ownership of the error information. If we aren't originating, and an error was already present,
|
||||
// then we need to restore the error information for later observation.
|
||||
SetRestrictedErrorInfo(restrictedErrorInformation.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace details
|
||||
} // namespace wil
|
||||
|
||||
// Automatically call RoOriginateError upon error origination by including this file
|
||||
WI_HEADER_INITITALIZATION_FUNCTION(ResultStowedExceptionInitialize, []
|
||||
{
|
||||
::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions);
|
||||
return 1;
|
||||
});
|
||||
#endif // __cplusplus_winrt
|
||||
|
||||
#endif // __WIL_RESULT_ORIGINATE_INCLUDED
|
206
Externals/WIL/include/wil/rpc_helpers.h
vendored
Normal file
206
Externals/WIL/include/wil/rpc_helpers.h
vendored
Normal file
@ -0,0 +1,206 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#ifndef __WIL_RPC_HELPERS_INCLUDED
|
||||
#define __WIL_RPC_HELPERS_INCLUDED
|
||||
|
||||
#include "result.h"
|
||||
#include "resource.h"
|
||||
#include "wistd_functional.h"
|
||||
#include "wistd_type_traits.h"
|
||||
|
||||
namespace wil
|
||||
{
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
// This call-adapter template converts a void-returning 'wistd::invoke' into
|
||||
// an HRESULT-returning 'wistd::invoke' that emits S_OK. It can be eliminated
|
||||
// with 'if constexpr' when C++17 is in wide use.
|
||||
template<typename TReturnType> struct call_adapter
|
||||
{
|
||||
template<typename... TArgs> static HRESULT call(TArgs&& ... args)
|
||||
{
|
||||
return wistd::invoke(wistd::forward<TArgs>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct call_adapter<void>
|
||||
{
|
||||
template<typename... TArgs> static HRESULT call(TArgs&& ... args)
|
||||
{
|
||||
wistd::invoke(wistd::forward<TArgs>(args)...);
|
||||
return S_OK;
|
||||
}
|
||||
};
|
||||
|
||||
// Some RPC exceptions are already HRESULTs. Others are in the regular Win32
|
||||
// error space. If the incoming exception code isn't an HRESULT, wrap it.
|
||||
constexpr HRESULT map_rpc_exception(DWORD code)
|
||||
{
|
||||
return IS_ERROR(code) ? code : __HRESULT_FROM_WIN32(code);
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
/** Invokes an RPC method, mapping structured exceptions to HRESULTs
|
||||
Failures encountered by the RPC infrastructure (such as server crashes, authentication
|
||||
errors, client parameter issues, etc.) are emitted by raising a structured exception from
|
||||
within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept,
|
||||
RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual
|
||||
flow control machinery to use.
|
||||
|
||||
Many RPC methods are defined as returning HRESULT themselves, where the HRESULT indicates
|
||||
the result of the _work_. HRESULTs returned by a successful completion of the _call_ are
|
||||
returned as-is.
|
||||
|
||||
RPC methods that have a return type of 'void' are mapped to returning S_OK when the _call_
|
||||
completes successfully.
|
||||
|
||||
For example, consider an RPC interface method defined in idl as:
|
||||
~~~
|
||||
HRESULT GetKittenState([in, ref, string] const wchar_t* name, [out, retval] KittenState** state);
|
||||
~~~
|
||||
To call this method, use:
|
||||
~~~
|
||||
wil::unique_rpc_binding binding = // typically gotten elsewhere;
|
||||
wil::unique_midl_ptr<KittenState> state;
|
||||
HRESULT hr = wil::invoke_rpc_nothrow(GetKittenState, binding.get(), L"fluffy", state.put());
|
||||
RETURN_IF_FAILED(hr);
|
||||
~~~
|
||||
*/
|
||||
template<typename... TCall> HRESULT invoke_rpc_nothrow(TCall&&... args) WI_NOEXCEPT
|
||||
{
|
||||
RpcTryExcept
|
||||
{
|
||||
// Note: this helper type can be removed with C++17 enabled via
|
||||
// 'if constexpr(wistd::is_same_v<void, result_t>)'
|
||||
using result_t = typename wistd::__invoke_of<TCall...>::type;
|
||||
RETURN_IF_FAILED(details::call_adapter<result_t>::call(wistd::forward<TCall>(args)...));
|
||||
return S_OK;
|
||||
}
|
||||
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
|
||||
{
|
||||
RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
|
||||
}
|
||||
RpcEndExcept
|
||||
}
|
||||
|
||||
/** Invokes an RPC method, mapping structured exceptions to HRESULTs
|
||||
Failures encountered by the RPC infrastructure (such as server crashes, authentication
|
||||
errors, client parameter issues, etc.) are emitted by raising a structured exception from
|
||||
within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept,
|
||||
RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual
|
||||
flow control machinery to use.
|
||||
|
||||
Some RPC methods return results (such as a state enumeration or other value) directly in
|
||||
their signature. This adapter writes that result into a caller-provided object then
|
||||
returns S_OK.
|
||||
|
||||
For example, consider an RPC interface method defined in idl as:
|
||||
~~~
|
||||
GUID GetKittenId([in, ref, string] const wchar_t* name);
|
||||
~~~
|
||||
To call this method, use:
|
||||
~~~
|
||||
wil::unique_rpc_binding binding = // typically gotten elsewhere;
|
||||
GUID id;
|
||||
HRESULT hr = wil::invoke_rpc_result_nothrow(id, GetKittenId, binding.get(), L"fluffy");
|
||||
RETURN_IF_FAILED(hr);
|
||||
~~~
|
||||
*/
|
||||
template<typename TResult, typename... TCall> HRESULT invoke_rpc_result_nothrow(TResult& result, TCall&&... args) WI_NOEXCEPT
|
||||
{
|
||||
RpcTryExcept
|
||||
{
|
||||
result = wistd::invoke(wistd::forward<TCall>(args)...);
|
||||
return S_OK;
|
||||
}
|
||||
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
|
||||
{
|
||||
RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
|
||||
}
|
||||
RpcEndExcept
|
||||
}
|
||||
|
||||
namespace details
|
||||
{
|
||||
// Provides an adapter around calling the context-handle-close method on an
|
||||
// RPC interface, which itself is an RPC call.
|
||||
template<typename TStorage, typename close_fn_t, close_fn_t close_fn>
|
||||
struct rpc_closer_t
|
||||
{
|
||||
static void Close(TStorage arg) WI_NOEXCEPT
|
||||
{
|
||||
LOG_IF_FAILED(invoke_rpc_nothrow(close_fn, &arg));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Manages explicit RPC context handles
|
||||
Explicit RPC context handles are used in many RPC interfaces. Most interfaces with
|
||||
context handles have an explicit `FooClose([in, out] CONTEXT*)` method that lets
|
||||
the server close out the context handle. As the close method itself is an RPC call,
|
||||
it can fail and raise a structured exception.
|
||||
|
||||
This type routes the context-handle-specific `Close` call through the `invoke_rpc_nothrow`
|
||||
helper, ensuring correct cleanup and lifecycle management.
|
||||
~~~
|
||||
// Assume the interface has two methods:
|
||||
// HRESULT OpenFoo([in] handle_t binding, [out] FOO_CONTEXT*);
|
||||
// HRESULT UseFoo([in] FOO_CONTEXT context;
|
||||
// void CloseFoo([in, out] PFOO_CONTEXT);
|
||||
using unique_foo_context = wil::unique_rpc_context_handle<FOO_CONTEXT, decltype(&CloseFoo), CloseFoo>;
|
||||
unique_foo_context context;
|
||||
RETURN_IF_FAILED(wil::invoke_rpc_nothrow(OpenFoo, m_binding.get(), context.put()));
|
||||
RETURN_IF_FAILED(wil::invoke_rpc_nothrow(UseFoo, context.get()));
|
||||
context.reset();
|
||||
~~~
|
||||
*/
|
||||
template<typename TContext, typename close_fn_t, close_fn_t close_fn>
|
||||
using unique_rpc_context_handle = unique_any<TContext, decltype(&details::rpc_closer_t<TContext, close_fn_t, close_fn>::Close), details::rpc_closer_t<TContext, close_fn_t, close_fn>::Close>;
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
/** Invokes an RPC method, mapping structured exceptions to C++ exceptions
|
||||
See `wil::invoke_rpc_nothrow` for additional information. Failures during the _call_
|
||||
and those returned by the _method_ are mapped to HRESULTs and thrown inside a
|
||||
wil::ResultException. Using the example RPC method provided above:
|
||||
~~~
|
||||
wil::unique_midl_ptr<KittenState> state;
|
||||
wil::invoke_rpc(GetKittenState, binding.get(), L"fluffy", state.put());
|
||||
// use 'state'
|
||||
~~~
|
||||
*/
|
||||
template<typename... TCall> void invoke_rpc(TCall&& ... args)
|
||||
{
|
||||
THROW_IF_FAILED(invoke_rpc_nothrow(wistd::forward<TCall>(args)...));
|
||||
}
|
||||
|
||||
/** Invokes an RPC method, mapping structured exceptions to C++ exceptions
|
||||
See `wil::invoke_rpc_result_nothrow` for additional information. Failures during the
|
||||
_call_ are mapped to HRESULTs and thrown inside a `wil::ResultException`. Using the
|
||||
example RPC method provided above:
|
||||
~~~
|
||||
GUID id = wil::invoke_rpc_result(GetKittenId, binding.get());
|
||||
// use 'id'
|
||||
~~~
|
||||
*/
|
||||
template<typename... TCall> auto invoke_rpc_result(TCall&& ... args)
|
||||
{
|
||||
using result_t = typename wistd::__invoke_of<TCall...>::type;
|
||||
result_t result{};
|
||||
THROW_IF_FAILED(invoke_rpc_result_nothrow(result, wistd::forward<TCall>(args)...));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
369
Externals/WIL/include/wil/safecast.h
vendored
Normal file
369
Externals/WIL/include/wil/safecast.h
vendored
Normal file
@ -0,0 +1,369 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#ifndef __WIL_SAFECAST_INCLUDED
|
||||
#define __WIL_SAFECAST_INCLUDED
|
||||
|
||||
#include "result_macros.h"
|
||||
#include <intsafe.h>
|
||||
#include "wistd_config.h"
|
||||
#include "wistd_type_traits.h"
|
||||
|
||||
namespace wil
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
// Default error case for undefined conversions in intsafe.h
|
||||
template<typename OldT, typename NewT> constexpr wistd::nullptr_t intsafe_conversion = nullptr;
|
||||
|
||||
// is_known_safe_static_cast_v determines if a conversion is known to be safe or not. Known
|
||||
// safe conversions can be handled by static_cast, this includes conversions between the same
|
||||
// type, when the new type is larger than the old type but is not a signed to unsigned
|
||||
// conversion, and when the two types are the same size and signed/unsigned. All other
|
||||
// conversions will be assumed to be potentially unsafe, and the conversion must be handled
|
||||
// by intsafe and checked.
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_known_safe_static_cast_v =
|
||||
(sizeof(NewT) > sizeof(OldT) && !(wistd::is_signed_v<OldT> && wistd::is_unsigned_v<NewT>)) ||
|
||||
(sizeof(NewT) == sizeof(OldT) && ((wistd::is_signed_v<NewT> && wistd::is_signed_v<OldT>) || (wistd::is_unsigned_v<NewT> && wistd::is_unsigned_v<OldT>)));
|
||||
|
||||
// Helper template to determine that NewT and OldT are both integral types. The safe_cast
|
||||
// operation only supports conversions between integral types.
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool both_integral_v = wistd::is_integral<NewT>::value && wistd::is_integral<OldT>::value;
|
||||
|
||||
// Note on native wchar_t (__wchar_t):
|
||||
// Intsafe.h does not currently handle native wchar_t. When compiling with /Zc:wchar_t-, this is fine as wchar_t is
|
||||
// typedef'd to unsigned short. However, when compiling with /Zc:wchar_t or wchar_t as a native type, the lack of
|
||||
// support for native wchar_t in intsafe.h becomes an issue. To work around this, we treat native wchar_t as an
|
||||
// unsigned short when passing it to intsafe.h, because the two on the Windows platform are the same size and
|
||||
// share the same range according to MSDN. If the cast is to a native wchar_t, the result from intsafe.h is cast
|
||||
// to a native wchar_t.
|
||||
|
||||
// Intsafe does not have a defined conversion for native wchar_t
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool neither_native_wchar_v = !wistd::is_same<NewT, __wchar_t>::value && !wistd::is_same<OldT, __wchar_t>::value;
|
||||
|
||||
// Check to see if the cast is a conversion to native wchar_t
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_cast_to_wchar_v = wistd::is_same<NewT, __wchar_t>::value && !wistd::is_same<OldT, __wchar_t>::value;
|
||||
|
||||
// Check to see if the cast is a conversion from native wchar_t
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_cast_from_wchar_v = !wistd::is_same<NewT, __wchar_t>::value && wistd::is_same<OldT, __wchar_t>::value;
|
||||
|
||||
// Validate the conversion to be performed has a defined mapping to an intsafe conversion
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_supported_intsafe_cast_v = intsafe_conversion<OldT, NewT> != nullptr;
|
||||
|
||||
// True when the conversion is between integral types and can be handled by static_cast
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_supported_safe_static_cast_v = both_integral_v<NewT, OldT> && is_known_safe_static_cast_v<NewT, OldT>;
|
||||
|
||||
// True when the conversion is between integral types, does not involve native wchar, has
|
||||
// a mapped intsafe conversion, and is unsafe.
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_supported_unsafe_cast_no_wchar_v =
|
||||
both_integral_v<NewT, OldT> &&
|
||||
!is_known_safe_static_cast_v<NewT, OldT> &&
|
||||
neither_native_wchar_v<NewT, OldT> &&
|
||||
is_supported_intsafe_cast_v<NewT, OldT>;
|
||||
|
||||
// True when the conversion is between integral types, is a cast to native wchar_t, has
|
||||
// a mapped intsafe conversion, and is unsafe.
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_supported_unsafe_cast_to_wchar_v =
|
||||
both_integral_v<NewT, OldT> &&
|
||||
!is_known_safe_static_cast_v<NewT, OldT> &&
|
||||
is_cast_to_wchar_v<NewT, OldT> &&
|
||||
is_supported_intsafe_cast_v<unsigned short, OldT>;
|
||||
|
||||
// True when the conversion is between integral types, is a cast from native wchar_t, has
|
||||
// a mapped intsafe conversion, and is unsafe.
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_supported_unsafe_cast_from_wchar_v =
|
||||
both_integral_v<NewT, OldT> &&
|
||||
!is_known_safe_static_cast_v<NewT, OldT> &&
|
||||
is_cast_from_wchar_v<NewT, OldT> &&
|
||||
is_supported_intsafe_cast_v<NewT, unsigned short>;
|
||||
|
||||
// True when the conversion is supported and unsafe, and may or may not involve
|
||||
// native wchar_t.
|
||||
template <typename NewT, typename OldT>
|
||||
constexpr bool is_supported_unsafe_cast_v =
|
||||
is_supported_unsafe_cast_no_wchar_v<NewT, OldT> ||
|
||||
is_supported_unsafe_cast_to_wchar_v<NewT, OldT> ||
|
||||
is_supported_unsafe_cast_from_wchar_v<NewT, OldT>;
|
||||
|
||||
// True when T is any one of the primitive types that the variably sized types are defined as.
|
||||
template <typename T>
|
||||
constexpr bool is_potentially_variably_sized_type_v =
|
||||
wistd::is_same<T, int>::value ||
|
||||
wistd::is_same<T, unsigned int>::value ||
|
||||
wistd::is_same<T, long>::value ||
|
||||
wistd::is_same<T, unsigned long>::value ||
|
||||
wistd::is_same<T, __int64>::value ||
|
||||
wistd::is_same<T, unsigned __int64>::value;
|
||||
|
||||
// True when either type is potentialy variably sized (e.g. size_t, ptrdiff_t)
|
||||
template <typename OldT, typename NewT>
|
||||
constexpr bool is_potentially_variably_sized_cast_v =
|
||||
is_potentially_variably_sized_type_v<OldT> ||
|
||||
is_potentially_variably_sized_type_v<NewT>;
|
||||
|
||||
// Mappings of all conversions defined in intsafe.h to intsafe_conversion
|
||||
// Note: Uppercase types (UINT, DWORD, SIZE_T, etc) and architecture dependent types resolve
|
||||
// to the base types. The base types are used since they do not vary based on architecture.
|
||||
template<> constexpr auto intsafe_conversion<__int64, char> = LongLongToChar;
|
||||
template<> constexpr auto intsafe_conversion<__int64, int> = LongLongToInt;
|
||||
template<> constexpr auto intsafe_conversion<__int64, long> = LongLongToLong;
|
||||
template<> constexpr auto intsafe_conversion<__int64, short> = LongLongToShort;
|
||||
template<> constexpr auto intsafe_conversion<__int64, signed char> = LongLongToInt8;
|
||||
template<> constexpr auto intsafe_conversion<__int64, unsigned __int64> = LongLongToULongLong;
|
||||
template<> constexpr auto intsafe_conversion<__int64, unsigned char> = LongLongToUChar;
|
||||
template<> constexpr auto intsafe_conversion<__int64, unsigned int> = LongLongToUInt;
|
||||
template<> constexpr auto intsafe_conversion<__int64, unsigned long> = LongLongToULong;
|
||||
template<> constexpr auto intsafe_conversion<__int64, unsigned short> = LongLongToUShort;
|
||||
template<> constexpr auto intsafe_conversion<int, char> = IntToChar;
|
||||
template<> constexpr auto intsafe_conversion<int, short> = IntToShort;
|
||||
template<> constexpr auto intsafe_conversion<int, signed char> = IntToInt8;
|
||||
template<> constexpr auto intsafe_conversion<int, unsigned __int64> = IntToULongLong;
|
||||
template<> constexpr auto intsafe_conversion<int, unsigned char> = IntToUChar;
|
||||
template<> constexpr auto intsafe_conversion<int, unsigned int> = IntToUInt;
|
||||
template<> constexpr auto intsafe_conversion<int, unsigned long> = IntToULong;
|
||||
template<> constexpr auto intsafe_conversion<int, unsigned short> = IntToUShort;
|
||||
template<> constexpr auto intsafe_conversion<long, char> = LongToChar;
|
||||
template<> constexpr auto intsafe_conversion<long, int> = LongToInt;
|
||||
template<> constexpr auto intsafe_conversion<long, short> = LongToShort;
|
||||
template<> constexpr auto intsafe_conversion<long, signed char> = LongToInt8;
|
||||
template<> constexpr auto intsafe_conversion<long, unsigned __int64> = LongToULongLong;
|
||||
template<> constexpr auto intsafe_conversion<long, unsigned char> = LongToUChar;
|
||||
template<> constexpr auto intsafe_conversion<long, unsigned int> = LongToUInt;
|
||||
template<> constexpr auto intsafe_conversion<long, unsigned long> = LongToULong;
|
||||
template<> constexpr auto intsafe_conversion<long, unsigned short> = LongToUShort;
|
||||
template<> constexpr auto intsafe_conversion<short, char> = ShortToChar;
|
||||
template<> constexpr auto intsafe_conversion<short, signed char> = ShortToInt8;
|
||||
template<> constexpr auto intsafe_conversion<short, unsigned __int64> = ShortToULongLong;
|
||||
template<> constexpr auto intsafe_conversion<short, unsigned char> = ShortToUChar;
|
||||
template<> constexpr auto intsafe_conversion<short, unsigned int> = ShortToUInt;
|
||||
template<> constexpr auto intsafe_conversion<short, unsigned long> = ShortToULong;
|
||||
template<> constexpr auto intsafe_conversion<short, unsigned short> = ShortToUShort;
|
||||
template<> constexpr auto intsafe_conversion<signed char, unsigned __int64> = Int8ToULongLong;
|
||||
template<> constexpr auto intsafe_conversion<signed char, unsigned char> = Int8ToUChar;
|
||||
template<> constexpr auto intsafe_conversion<signed char, unsigned int> = Int8ToUInt;
|
||||
template<> constexpr auto intsafe_conversion<signed char, unsigned long> = Int8ToULong;
|
||||
template<> constexpr auto intsafe_conversion<signed char, unsigned short> = Int8ToUShort;
|
||||
template<> constexpr auto intsafe_conversion<unsigned __int64, __int64> = ULongLongToLongLong;
|
||||
template<> constexpr auto intsafe_conversion<unsigned __int64, char> = ULongLongToChar;
|
||||
template<> constexpr auto intsafe_conversion<unsigned __int64, int> = ULongLongToInt;
|
||||
template<> constexpr auto intsafe_conversion<unsigned __int64, long> = ULongLongToLong;
|
||||
template<> constexpr auto intsafe_conversion<unsigned __int64, short> = ULongLongToShort;
|
||||
template<> constexpr auto intsafe_conversion<unsigned __int64, signed char> = ULongLongToInt8;
|
||||
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned char> = ULongLongToUChar;
|
||||
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned int> = ULongLongToUInt;
|
||||
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned long> = ULongLongToULong;
|
||||
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned short> = ULongLongToUShort;
|
||||
template<> constexpr auto intsafe_conversion<unsigned char, char> = UInt8ToChar;
|
||||
template<> constexpr auto intsafe_conversion<unsigned char, signed char> = UIntToInt8;
|
||||
template<> constexpr auto intsafe_conversion<unsigned int, char> = UIntToChar;
|
||||
template<> constexpr auto intsafe_conversion<unsigned int, int> = UIntToInt;
|
||||
template<> constexpr auto intsafe_conversion<unsigned int, long> = UIntToLong;
|
||||
template<> constexpr auto intsafe_conversion<unsigned int, short> = UIntToShort;
|
||||
template<> constexpr auto intsafe_conversion<unsigned int, signed char> = UIntToInt8;
|
||||
template<> constexpr auto intsafe_conversion<unsigned int, unsigned char> = UIntToUChar;
|
||||
template<> constexpr auto intsafe_conversion<unsigned int, unsigned short> = UIntToUShort;
|
||||
template<> constexpr auto intsafe_conversion<unsigned long, char> = ULongToChar;
|
||||
template<> constexpr auto intsafe_conversion<unsigned long, int> = ULongToInt;
|
||||
template<> constexpr auto intsafe_conversion<unsigned long, long> = ULongToLong;
|
||||
template<> constexpr auto intsafe_conversion<unsigned long, short> = ULongToShort;
|
||||
template<> constexpr auto intsafe_conversion<unsigned long, signed char> = ULongToInt8;
|
||||
template<> constexpr auto intsafe_conversion<unsigned long, unsigned char> = ULongToUChar;
|
||||
template<> constexpr auto intsafe_conversion<unsigned long, unsigned int> = ULongToUInt;
|
||||
template<> constexpr auto intsafe_conversion<unsigned long, unsigned short> = ULongToUShort;
|
||||
template<> constexpr auto intsafe_conversion<unsigned short, char> = UShortToChar;
|
||||
template<> constexpr auto intsafe_conversion<unsigned short, short> = UShortToShort;
|
||||
template<> constexpr auto intsafe_conversion<unsigned short, signed char> = UShortToInt8;
|
||||
template<> constexpr auto intsafe_conversion<unsigned short, unsigned char> = UShortToUChar;
|
||||
}
|
||||
|
||||
// Unsafe conversion where failure results in fail fast.
|
||||
template <
|
||||
typename NewT,
|
||||
typename OldT,
|
||||
wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
|
||||
>
|
||||
NewT safe_cast_failfast(const OldT var)
|
||||
{
|
||||
NewT newVar;
|
||||
FAIL_FAST_IF_FAILED((details::intsafe_conversion<OldT, NewT>(var, &newVar)));
|
||||
return newVar;
|
||||
}
|
||||
|
||||
// Unsafe conversion where failure results in fail fast.
|
||||
template <
|
||||
typename NewT,
|
||||
typename OldT,
|
||||
wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
|
||||
>
|
||||
NewT safe_cast_failfast(const OldT var)
|
||||
{
|
||||
NewT newVar;
|
||||
FAIL_FAST_IF_FAILED((details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), &newVar)));
|
||||
return newVar;
|
||||
}
|
||||
|
||||
// Unsafe conversion where failure results in fail fast.
|
||||
template <
|
||||
typename NewT,
|
||||
typename OldT,
|
||||
wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
|
||||
>
|
||||
NewT safe_cast_failfast(const OldT var)
|
||||
{
|
||||
unsigned short newVar;
|
||||
FAIL_FAST_IF_FAILED((details::intsafe_conversion<OldT, unsigned short>(var, &newVar)));
|
||||
return static_cast<__wchar_t>(newVar);
|
||||
}
|
||||
|
||||
// This conversion is always safe, therefore a static_cast is fine.
|
||||
template <
|
||||
typename NewT,
|
||||
typename OldT,
|
||||
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
|
||||
>
|
||||
NewT safe_cast_failfast(const OldT var)
|
||||
{
|
||||
return static_cast<NewT>(var);
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
// Unsafe conversion where failure results in a thrown exception.
|
||||
template <
|
||||
typename NewT,
|
||||
typename OldT,
|
||||
wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
|
||||
>
|
||||
NewT safe_cast(const OldT var)
|
||||
{
|
||||
NewT newVar;
|
||||
THROW_IF_FAILED((details::intsafe_conversion<OldT, NewT>(var, &newVar)));
|
||||
return newVar;
|
||||
}
|
||||
|
||||
// Unsafe conversion where failure results in a thrown exception.
|
||||
template <
|
||||
typename NewT,
|
||||
typename OldT,
|
||||
wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
|
||||
>
|
||||
NewT safe_cast(const OldT var)
|
||||
{
|
||||
NewT newVar;
|
||||
THROW_IF_FAILED((details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), &newVar)));
|
||||
return newVar;
|
||||
}
|
||||
|
||||
// Unsafe conversion where failure results in a thrown exception.
|
||||
template <
|
||||
typename NewT,
|
||||
typename OldT,
|
||||
wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
|
||||
>
|
||||
NewT safe_cast(const OldT var)
|
||||
{
|
||||
unsigned short newVar;
|
||||
THROW_IF_FAILED((details::intsafe_conversion<OldT, unsigned short>(var, &newVar)));
|
||||
return static_cast<__wchar_t>(newVar);
|
||||
}
|
||||
|
||||
// This conversion is always safe, therefore a static_cast is fine.
|
||||
template <
|
||||
typename NewT,
|
||||
typename OldT,
|
||||
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
|
||||
>
|
||||
NewT safe_cast(const OldT var)
|
||||
{
|
||||
return static_cast<NewT>(var);
|
||||
}
|
||||
#endif
|
||||
|
||||
// This conversion is unsafe, therefore the two parameter version of safe_cast_nothrow must be used
|
||||
template <
|
||||
typename NewT,
|
||||
typename OldT,
|
||||
wistd::enable_if_t<details::is_supported_unsafe_cast_v<NewT, OldT>, int> = 0
|
||||
>
|
||||
NewT safe_cast_nothrow(const OldT /*var*/)
|
||||
{
|
||||
static_assert(!wistd::is_same_v<NewT, NewT>, "This cast has the potential to fail, use the two parameter safe_cast_nothrow instead");
|
||||
}
|
||||
|
||||
// This conversion is always safe, therefore a static_cast is fine.
|
||||
template <
|
||||
typename NewT,
|
||||
typename OldT,
|
||||
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
|
||||
>
|
||||
NewT safe_cast_nothrow(const OldT var)
|
||||
{
|
||||
return static_cast<NewT>(var);
|
||||
}
|
||||
|
||||
// Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
|
||||
template <
|
||||
typename NewT,
|
||||
typename OldT,
|
||||
wistd::enable_if_t<details::is_supported_unsafe_cast_no_wchar_v<NewT, OldT>, int> = 0
|
||||
>
|
||||
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
|
||||
{
|
||||
return details::intsafe_conversion<OldT, NewT>(var, newTResult);
|
||||
}
|
||||
|
||||
// Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
|
||||
template <
|
||||
typename NewT,
|
||||
typename OldT,
|
||||
wistd::enable_if_t<details::is_supported_unsafe_cast_from_wchar_v<NewT, OldT>, int> = 0
|
||||
>
|
||||
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
|
||||
{
|
||||
return details::intsafe_conversion<unsigned short, NewT>(static_cast<unsigned short>(var), newTResult);
|
||||
}
|
||||
|
||||
// Unsafe conversion where an HRESULT is returned. It is up to the callee to check and handle the HRESULT
|
||||
template <
|
||||
typename NewT,
|
||||
typename OldT,
|
||||
wistd::enable_if_t<details::is_supported_unsafe_cast_to_wchar_v<NewT, OldT>, int> = 0
|
||||
>
|
||||
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
|
||||
{
|
||||
return details::intsafe_conversion<OldT, unsigned short>(var, reinterpret_cast<unsigned short *>(newTResult));
|
||||
}
|
||||
|
||||
// This conversion is always safe, therefore a static_cast is fine. If it can be determined the conversion
|
||||
// does not involve a variably sized type, then the compilation will fail and say the single parameter version
|
||||
// of safe_cast_nothrow should be used instead.
|
||||
template <
|
||||
typename NewT,
|
||||
typename OldT,
|
||||
wistd::enable_if_t<details::is_supported_safe_static_cast_v<NewT, OldT>, int> = 0
|
||||
>
|
||||
HRESULT safe_cast_nothrow(const OldT var, NewT* newTResult)
|
||||
{
|
||||
static_assert(details::is_potentially_variably_sized_cast_v<OldT, NewT>, "This cast is always safe; use safe_cast_nothrow<T>(value) to avoid unnecessary error handling.");
|
||||
*newTResult = static_cast<NewT>(var);
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // __WIL_SAFECAST_INCLUDED
|
124
Externals/WIL/include/wil/stl.h
vendored
Normal file
124
Externals/WIL/include/wil/stl.h
vendored
Normal file
@ -0,0 +1,124 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#ifndef __WIL_STL_INCLUDED
|
||||
#define __WIL_STL_INCLUDED
|
||||
|
||||
#include "common.h"
|
||||
#include "resource.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#if defined(WIL_ENABLE_EXCEPTIONS)
|
||||
namespace std
|
||||
{
|
||||
template<class _Ty, class _Alloc>
|
||||
class vector;
|
||||
|
||||
template<class _Elem>
|
||||
struct char_traits;
|
||||
|
||||
template<class _Elem, class _Traits, class _Alloc>
|
||||
class basic_string;
|
||||
} // namespace std
|
||||
|
||||
namespace wil
|
||||
{
|
||||
/** Secure allocator for STL containers.
|
||||
The `wil::secure_allocator` allocator calls `SecureZeroMemory` before deallocating
|
||||
memory. This provides a mechanism for secure STL containers such as `wil::secure_vector`,
|
||||
`wil::secure_string`, and `wil::secure_wstring`. */
|
||||
template <typename T>
|
||||
struct secure_allocator
|
||||
: public std::allocator<T>
|
||||
{
|
||||
template<typename Other>
|
||||
struct rebind
|
||||
{
|
||||
typedef secure_allocator<Other> other;
|
||||
};
|
||||
|
||||
secure_allocator()
|
||||
: std::allocator<T>()
|
||||
{
|
||||
}
|
||||
|
||||
~secure_allocator() = default;
|
||||
|
||||
secure_allocator(const secure_allocator& a)
|
||||
: std::allocator<T>(a)
|
||||
{
|
||||
}
|
||||
|
||||
template <class U>
|
||||
secure_allocator(const secure_allocator<U>& a)
|
||||
: std::allocator<T>(a)
|
||||
{
|
||||
}
|
||||
|
||||
T* allocate(size_t n)
|
||||
{
|
||||
return std::allocator<T>::allocate(n);
|
||||
}
|
||||
|
||||
void deallocate(T* p, size_t n)
|
||||
{
|
||||
SecureZeroMemory(p, sizeof(T) * n);
|
||||
std::allocator<T>::deallocate(p, n);
|
||||
}
|
||||
};
|
||||
|
||||
//! `wil::secure_vector` will be securely zeroed before deallocation.
|
||||
template <typename Type>
|
||||
using secure_vector = std::vector<Type, secure_allocator<Type>>;
|
||||
//! `wil::secure_wstring` will be securely zeroed before deallocation.
|
||||
using secure_wstring = std::basic_string<wchar_t, std::char_traits<wchar_t>, wil::secure_allocator<wchar_t>>;
|
||||
//! `wil::secure_string` will be securely zeroed before deallocation.
|
||||
using secure_string = std::basic_string<char, std::char_traits<char>, wil::secure_allocator<char>>;
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
template<> struct string_maker<std::wstring>
|
||||
{
|
||||
HRESULT make(_In_reads_opt_(length) PCWSTR source, size_t length) WI_NOEXCEPT try
|
||||
{
|
||||
m_value = source ? std::wstring(source, length) : std::wstring(length, L'\0');
|
||||
return S_OK;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
wchar_t* buffer() { return &m_value[0]; }
|
||||
|
||||
std::wstring release() { return std::wstring(std::move(m_value)); }
|
||||
|
||||
static PCWSTR get(const std::wstring& value) { return value.c_str(); }
|
||||
|
||||
private:
|
||||
std::wstring m_value;
|
||||
};
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
// str_raw_ptr is an overloaded function that retrieves a const pointer to the first character in a string's buffer.
|
||||
// This is the overload for std::wstring. Other overloads available in resource.h.
|
||||
inline PCWSTR str_raw_ptr(const std::wstring& str)
|
||||
{
|
||||
return str.c_str();
|
||||
}
|
||||
|
||||
} // namespace wil
|
||||
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
#endif // __WIL_STL_INCLUDED
|
597
Externals/WIL/include/wil/token_helpers.h
vendored
Normal file
597
Externals/WIL/include/wil/token_helpers.h
vendored
Normal file
@ -0,0 +1,597 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#ifndef __WIL_TOKEN_HELPERS_INCLUDED
|
||||
#define __WIL_TOKEN_HELPERS_INCLUDED
|
||||
|
||||
#ifdef _KERNEL_MODE
|
||||
#error This header is not supported in kernel-mode.
|
||||
#endif
|
||||
|
||||
#include "resource.h"
|
||||
#include <new>
|
||||
#include <lmcons.h> // for UNLEN and DNLEN
|
||||
#include <processthreadsapi.h>
|
||||
|
||||
// for GetUserNameEx()
|
||||
#define SECURITY_WIN32
|
||||
#include <Security.h>
|
||||
|
||||
namespace wil
|
||||
{
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
// Template specialization for TOKEN_INFORMATION_CLASS, add more mappings here as needed
|
||||
// TODO: The mapping should be reversed to be MapTokenInfoClassToStruct since there may
|
||||
// be an info class value that uses the same structure. That is the case for the file
|
||||
// system information.
|
||||
template<typename T> struct MapTokenStructToInfoClass;
|
||||
template<> struct MapTokenStructToInfoClass<TOKEN_ACCESS_INFORMATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenAccessInformation; static const bool FixedSize = false; };
|
||||
template<> struct MapTokenStructToInfoClass<TOKEN_APPCONTAINER_INFORMATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenAppContainerSid; static const bool FixedSize = false; };
|
||||
template<> struct MapTokenStructToInfoClass<TOKEN_DEFAULT_DACL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenDefaultDacl; static const bool FixedSize = false; };
|
||||
template<> struct MapTokenStructToInfoClass<TOKEN_GROUPS_AND_PRIVILEGES> { static const TOKEN_INFORMATION_CLASS infoClass = TokenGroupsAndPrivileges; static const bool FixedSize = false; };
|
||||
template<> struct MapTokenStructToInfoClass<TOKEN_MANDATORY_LABEL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenIntegrityLevel; static const bool FixedSize = false; };
|
||||
template<> struct MapTokenStructToInfoClass<TOKEN_OWNER> { static const TOKEN_INFORMATION_CLASS infoClass = TokenOwner; static const bool FixedSize = false; };
|
||||
template<> struct MapTokenStructToInfoClass<TOKEN_PRIMARY_GROUP> { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrimaryGroup; static const bool FixedSize = false; };
|
||||
template<> struct MapTokenStructToInfoClass<TOKEN_PRIVILEGES> { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrivileges; static const bool FixedSize = false; };
|
||||
template<> struct MapTokenStructToInfoClass<TOKEN_USER> { static const TOKEN_INFORMATION_CLASS infoClass = TokenUser; static const bool FixedSize = false; };
|
||||
|
||||
// fixed size cases
|
||||
template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION_TYPE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevationType; static const bool FixedSize = true; };
|
||||
template<> struct MapTokenStructToInfoClass<TOKEN_MANDATORY_POLICY> { static const TOKEN_INFORMATION_CLASS infoClass = TokenMandatoryPolicy; static const bool FixedSize = true; };
|
||||
template<> struct MapTokenStructToInfoClass<TOKEN_ORIGIN> { static const TOKEN_INFORMATION_CLASS infoClass = TokenOrigin; static const bool FixedSize = true; };
|
||||
template<> struct MapTokenStructToInfoClass<TOKEN_SOURCE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenSource; static const bool FixedSize = true; };
|
||||
template<> struct MapTokenStructToInfoClass<TOKEN_STATISTICS> { static const TOKEN_INFORMATION_CLASS infoClass = TokenStatistics; static const bool FixedSize = true; };
|
||||
template<> struct MapTokenStructToInfoClass<TOKEN_TYPE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenType; static const bool FixedSize = true; };
|
||||
template<> struct MapTokenStructToInfoClass<SECURITY_IMPERSONATION_LEVEL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenImpersonationLevel; static const bool FixedSize = true; };
|
||||
template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevation; static const bool FixedSize = true; };
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
enum class OpenThreadTokenAs
|
||||
{
|
||||
Current,
|
||||
Self
|
||||
};
|
||||
|
||||
/** Open the active token.
|
||||
Opens either the current thread token (if impersonating) or the current process token. Returns a token the caller
|
||||
can use with methods like get_token_information<> below. By default, the token is opened for TOKEN_QUERY and as the
|
||||
effective user.
|
||||
|
||||
Consider using GetCurrentThreadEffectiveToken() instead of this method when eventually calling get_token_information.
|
||||
This method returns a real handle to the effective token, but GetCurrentThreadEffectiveToken() is a Pseudo-handle
|
||||
and much easier to manage.
|
||||
~~~~
|
||||
wil::unique_handle theToken;
|
||||
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken));
|
||||
~~~~
|
||||
Callers who want more access to the token (such as to duplicate or modify the token) can pass
|
||||
any mask of the token rights.
|
||||
~~~~
|
||||
wil::unique_handle theToken;
|
||||
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES));
|
||||
~~~~
|
||||
Services impersonating their clients may need to request that the active token is opened on the
|
||||
behalf of the service process to perform certain operations. Opening a token for impersonation access
|
||||
or privilege-adjustment are examples of uses.
|
||||
~~~~
|
||||
wil::unique_handle callerToken;
|
||||
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_IMPERSONATE, true));
|
||||
~~~~
|
||||
@param tokenHandle Receives the token opened during the operation. Must be CloseHandle'd by the caller, or
|
||||
(preferably) stored in a wil::unique_handle
|
||||
@param access Bits from the TOKEN_* access mask which are passed to OpenThreadToken/OpenProcessToken
|
||||
@param asSelf When true, and if the thread is impersonating, the thread token is opened using the
|
||||
process token's rights.
|
||||
*/
|
||||
inline HRESULT open_current_access_token_nothrow(_Out_ HANDLE* tokenHandle, unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
|
||||
{
|
||||
HRESULT hr = (OpenThreadToken(GetCurrentThread(), access, (openAs == OpenThreadTokenAs::Self), tokenHandle) ? S_OK : HRESULT_FROM_WIN32(::GetLastError()));
|
||||
if (hr == HRESULT_FROM_WIN32(ERROR_NO_TOKEN))
|
||||
{
|
||||
hr = (OpenProcessToken(GetCurrentProcess(), access, tokenHandle) ? S_OK : HRESULT_FROM_WIN32(::GetLastError()));
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
//! Current thread or process token, consider using GetCurrentThreadEffectiveToken() instead.
|
||||
inline wil::unique_handle open_current_access_token_failfast(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
|
||||
{
|
||||
HANDLE rawTokenHandle;
|
||||
FAIL_FAST_IF_FAILED(open_current_access_token_nothrow(&rawTokenHandle, access, openAs));
|
||||
return wil::unique_handle(rawTokenHandle);
|
||||
}
|
||||
|
||||
// Exception based function to open current thread/process access token and acquire pointer to it
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
//! Current thread or process token, consider using GetCurrentThreadEffectiveToken() instead.
|
||||
inline wil::unique_handle open_current_access_token(unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
|
||||
{
|
||||
HANDLE rawTokenHandle;
|
||||
THROW_IF_FAILED(open_current_access_token_nothrow(&rawTokenHandle, access, openAs));
|
||||
return wil::unique_handle(rawTokenHandle);
|
||||
}
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
// Returns tokenHandle or the effective thread token if tokenHandle is null.
|
||||
// Note, this returns an token handle who's lifetime is managed independently
|
||||
// and it may be a pseudo token, don't free it!
|
||||
inline HANDLE GetCurrentThreadEffectiveTokenWithOverride(HANDLE tokenHandle)
|
||||
{
|
||||
return tokenHandle ? tokenHandle : GetCurrentThreadEffectiveToken();
|
||||
}
|
||||
|
||||
/** Fetches information about a token.
|
||||
See GetTokenInformation on MSDN for what this method can return. For variable sized structs the information
|
||||
is returned to the caller as a wistd::unique_ptr<T> (like TOKEN_ORIGIN, TOKEN_USER, TOKEN_ELEVATION, etc.). For
|
||||
fixed sized, the struct is returned directly.
|
||||
The caller must have access to read the information from the provided token. This method works with both real
|
||||
(e.g. OpenCurrentAccessToken) and pseudo (e.g. GetCurrentThreadToken) token handles.
|
||||
~~~~
|
||||
// Retrieve the TOKEN_USER structure for the current process
|
||||
wistd::unique_ptr<TOKEN_USER> user;
|
||||
RETURN_IF_FAILED(wil::get_token_information_nothrow(user, GetCurrentProcessToken()));
|
||||
RETURN_IF_FAILED(ConsumeSid(user->User.Sid));
|
||||
~~~~
|
||||
Not specifying the token handle is the same as specifying 'nullptr' and retrieves information about the effective token.
|
||||
~~~~
|
||||
wistd::unique_ptr<TOKEN_PRIVILEGES> privileges;
|
||||
RETURN_IF_FAILED(wil::get_token_information_nothrow(privileges));
|
||||
for (auto const& privilege : wil::GetRange(privileges->Privileges, privileges->PrivilegeCount))
|
||||
{
|
||||
RETURN_IF_FAILED(ConsumePrivilege(privilege));
|
||||
}
|
||||
~~~~
|
||||
@param tokenInfo Receives a pointer to a structure containing the results of GetTokenInformation for the requested
|
||||
type. The type of <T> selects which TOKEN_INFORMATION_CLASS will be used.
|
||||
@param tokenHandle Specifies which token will be queried. When nullptr, the thread's effective current token is used.
|
||||
@return S_OK on success, a FAILED hresult containing the win32 error from querying the token otherwise.
|
||||
*/
|
||||
|
||||
template <typename T, wistd::enable_if_t<!details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
|
||||
inline HRESULT get_token_information_nothrow(wistd::unique_ptr<T>& tokenInfo, HANDLE tokenHandle = nullptr)
|
||||
{
|
||||
tokenInfo.reset();
|
||||
tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle);
|
||||
|
||||
DWORD tokenInfoSize = 0;
|
||||
const auto infoClass = details::MapTokenStructToInfoClass<T>::infoClass;
|
||||
RETURN_LAST_ERROR_IF(!((!GetTokenInformation(tokenHandle, infoClass, nullptr, 0, &tokenInfoSize)) &&
|
||||
(::GetLastError() == ERROR_INSUFFICIENT_BUFFER)));
|
||||
wistd::unique_ptr<char> tokenInfoClose(
|
||||
static_cast<char*>(operator new(tokenInfoSize, std::nothrow)));
|
||||
RETURN_IF_NULL_ALLOC(tokenInfoClose.get());
|
||||
RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfoClose.get(), tokenInfoSize, &tokenInfoSize));
|
||||
tokenInfo.reset(reinterpret_cast<T *>(tokenInfoClose.release()));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
template <typename T, wistd::enable_if_t<details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
|
||||
inline HRESULT get_token_information_nothrow(_Out_ T* tokenInfo, HANDLE tokenHandle = nullptr)
|
||||
{
|
||||
*tokenInfo = {};
|
||||
tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle);
|
||||
|
||||
DWORD tokenInfoSize = sizeof(T);
|
||||
const auto infoClass = details::MapTokenStructToInfoClass<T>::infoClass;
|
||||
RETURN_IF_WIN32_BOOL_FALSE(GetTokenInformation(tokenHandle, infoClass, tokenInfo, tokenInfoSize, &tokenInfoSize));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
namespace details
|
||||
{
|
||||
template<typename T, typename policy, wistd::enable_if_t<!details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
|
||||
wistd::unique_ptr<T> GetTokenInfoWrap(HANDLE token = nullptr)
|
||||
{
|
||||
wistd::unique_ptr<T> temp;
|
||||
policy::HResult(get_token_information_nothrow(temp, token));
|
||||
return temp;
|
||||
}
|
||||
|
||||
template<typename T, typename policy, wistd::enable_if_t<details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
|
||||
T GetTokenInfoWrap(HANDLE token = nullptr)
|
||||
{
|
||||
T temp{};
|
||||
policy::HResult(get_token_information_nothrow(&temp, token));
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
|
||||
//! A variant of get_token_information<T> that fails-fast on errors retrieving the token
|
||||
template <typename T>
|
||||
inline auto get_token_information_failfast(HANDLE token = nullptr)
|
||||
{
|
||||
return details::GetTokenInfoWrap<T, err_failfast_policy>(token);
|
||||
}
|
||||
|
||||
//! Overload of GetTokenInformationNoThrow that retrieves a token linked from the provided token
|
||||
inline HRESULT get_token_information_nothrow(unique_token_linked_token& tokenInfo, HANDLE tokenHandle = nullptr)
|
||||
{
|
||||
static_assert(sizeof(tokenInfo) == sizeof(TOKEN_LINKED_TOKEN), "confusing size mismatch");
|
||||
tokenHandle = GetCurrentThreadEffectiveTokenWithOverride(tokenHandle);
|
||||
|
||||
DWORD tokenInfoSize = 0;
|
||||
RETURN_IF_WIN32_BOOL_FALSE(::GetTokenInformation(tokenHandle, TokenLinkedToken,
|
||||
tokenInfo.reset_and_addressof(), sizeof(tokenInfo), &tokenInfoSize));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/** Retrieves the linked-token information for a token.
|
||||
Fails-fast if the link information cannot be retrieved.
|
||||
~~~~
|
||||
auto link = get_linked_token_information_failfast(GetCurrentThreadToken());
|
||||
auto tokenUser = get_token_information<TOKEN_USER>(link.LinkedToken);
|
||||
~~~~
|
||||
@param token Specifies the token to query. Pass nullptr to use the current effective thread token
|
||||
@return unique_token_linked_token containing a handle to the linked token
|
||||
*/
|
||||
inline unique_token_linked_token get_linked_token_information_failfast(HANDLE token = nullptr)
|
||||
{
|
||||
unique_token_linked_token tokenInfo;
|
||||
FAIL_FAST_IF_FAILED(get_token_information_nothrow(tokenInfo, token));
|
||||
return tokenInfo;
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
/** Fetches information about a token.
|
||||
See get_token_information_nothrow for full details.
|
||||
~~~~
|
||||
auto user = wil::get_token_information<TOKEN_USER>(GetCurrentProcessToken());
|
||||
ConsumeSid(user->User.Sid);
|
||||
~~~~
|
||||
Pass 'nullptr' (or omit the parameter) as tokenHandle to retrieve information about the effective token.
|
||||
~~~~
|
||||
auto privs = wil::get_token_information<TOKEN_PRIVILEGES>(privileges);
|
||||
for (auto& priv : wil::make_range(privs->Privileges, privs->Privilieges + privs->PrivilegeCount))
|
||||
{
|
||||
if (priv.Attributes & SE_PRIVILEGE_ENABLED)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
}
|
||||
~~~~
|
||||
@return A pointer to a structure containing the results of GetTokenInformation for the requested type. The type of
|
||||
<T> selects which TOKEN_INFORMATION_CLASS will be used.
|
||||
@param token Specifies which token will be queried. When nullptr or not set, the thread's effective current token is used.
|
||||
*/
|
||||
template <typename T>
|
||||
inline auto get_token_information(HANDLE token = nullptr)
|
||||
{
|
||||
return details::GetTokenInfoWrap<T, err_exception_policy>(token);
|
||||
}
|
||||
|
||||
/** Retrieves the linked-token information for a token.
|
||||
Throws an exception if the link information cannot be retrieved.
|
||||
~~~~
|
||||
auto link = get_linked_token_information(GetCurrentThreadToken());
|
||||
auto tokenUser = get_token_information<TOKEN_USER>(link.LinkedToken);
|
||||
~~~~
|
||||
@param token Specifies the token to query. Pass nullptr to use the current effective thread token
|
||||
@return unique_token_linked_token containing a handle to the linked token
|
||||
*/
|
||||
inline unique_token_linked_token get_linked_token_information(HANDLE token = nullptr)
|
||||
{
|
||||
unique_token_linked_token tokenInfo;
|
||||
THROW_IF_FAILED(get_token_information_nothrow(tokenInfo, token));
|
||||
return tokenInfo;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
inline void RevertImpersonateToken(_Pre_opt_valid_ _Frees_ptr_opt_ HANDLE oldToken)
|
||||
{
|
||||
FAIL_FAST_IMMEDIATE_IF(!::SetThreadToken(nullptr, oldToken));
|
||||
|
||||
if (oldToken)
|
||||
{
|
||||
::CloseHandle(oldToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
using unique_token_reverter = wil::unique_any<
|
||||
HANDLE,
|
||||
decltype(&details::RevertImpersonateToken),
|
||||
details::RevertImpersonateToken,
|
||||
details::pointer_access_none,
|
||||
HANDLE,
|
||||
INT_PTR,
|
||||
-1,
|
||||
HANDLE>;
|
||||
|
||||
/** Temporarily impersonates a token on this thread.
|
||||
This method sets a new token on a thread, restoring the current token when the returned object
|
||||
is destroyed. Useful for impersonating other tokens or running as 'self,' especially in services.
|
||||
~~~~
|
||||
HRESULT OpenFileAsSessionuser(PCWSTR filePath, DWORD session, _Out_ HANDLE* opened)
|
||||
{
|
||||
wil::unique_handle userToken;
|
||||
RETURN_IF_WIN32_BOOL_FALSE(QueryUserToken(session, &userToken));
|
||||
|
||||
wil::unique_token_reverter reverter;
|
||||
RETURN_IF_FAILED(wil::impersonate_token_nothrow(userToken.get(), reverter));
|
||||
|
||||
wil::unique_hfile userFile(::CreateFile(filePath, ...));
|
||||
RETURN_LAST_ERROR_IF(!userFile && (::GetLastError() != ERROR_FILE_NOT_FOUND));
|
||||
|
||||
*opened = userFile.release();
|
||||
return S_OK;
|
||||
}
|
||||
~~~~
|
||||
@param token A token to impersonate, or 'nullptr' to run as the process identity.
|
||||
*/
|
||||
inline HRESULT impersonate_token_nothrow(HANDLE token, unique_token_reverter& reverter)
|
||||
{
|
||||
wil::unique_handle currentToken;
|
||||
|
||||
// Get the token for the current thread. If there wasn't one, the reset will clear it as well
|
||||
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, ¤tToken))
|
||||
{
|
||||
RETURN_LAST_ERROR_IF(::GetLastError() != ERROR_NO_TOKEN);
|
||||
}
|
||||
|
||||
// Update the current token
|
||||
RETURN_IF_WIN32_BOOL_FALSE(::SetThreadToken(nullptr, token));
|
||||
|
||||
reverter.reset(currentToken.release()); // Ownership passed
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/** Temporarily clears any impersonation on this thread.
|
||||
This method resets the current thread's token to nullptr, indicating that it is not impersonating
|
||||
any user. Useful for elevating to whatever identity a service or higher-privilege process might
|
||||
be capable of running under.
|
||||
~~~~
|
||||
HRESULT DeleteFileRetryAsSelf(PCWSTR filePath)
|
||||
{
|
||||
if (!::DeleteFile(filePath))
|
||||
{
|
||||
RETURN_LAST_ERROR_IF(::GetLastError() != ERROR_ACCESS_DENIED);
|
||||
wil::unique_token_reverter reverter;
|
||||
RETURN_IF_FAILED(wil::run_as_self_nothrow(reverter));
|
||||
RETURN_IF_FAILED(TakeOwnershipOfFile(filePath));
|
||||
RETURN_IF_FAILED(GrantDeleteAccess(filePath));
|
||||
RETURN_IF_WIN32_BOOL_FALSE(::DeleteFile(filePath));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
~~~~
|
||||
*/
|
||||
inline HRESULT run_as_self_nothrow(unique_token_reverter& reverter)
|
||||
{
|
||||
return impersonate_token_nothrow(nullptr, reverter);
|
||||
}
|
||||
|
||||
inline unique_token_reverter impersonate_token_failfast(HANDLE token)
|
||||
{
|
||||
unique_token_reverter oldToken;
|
||||
FAIL_FAST_IF_FAILED(impersonate_token_nothrow(token, oldToken));
|
||||
return oldToken;
|
||||
}
|
||||
|
||||
inline unique_token_reverter run_as_self_failfast()
|
||||
{
|
||||
return impersonate_token_failfast(nullptr);
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
/** Temporarily impersonates a token on this thread.
|
||||
This method sets a new token on a thread, restoring the current token when the returned object
|
||||
is destroyed. Useful for impersonating other tokens or running as 'self,' especially in services.
|
||||
~~~~
|
||||
wil::unique_hfile OpenFileAsSessionuser(_In_z_ const wchar_t* filePath, DWORD session)
|
||||
{
|
||||
wil::unique_handle userToken;
|
||||
THROW_IF_WIN32_BOOL_FALSE(QueryUserToken(session, &userToken));
|
||||
|
||||
auto priorToken = wil::impersonate_token(userToken.get());
|
||||
|
||||
wil::unique_hfile userFile(::CreateFile(filePath, ...));
|
||||
THROW_LAST_ERROR_IF(::GetLastError() != ERROR_FILE_NOT_FOUND);
|
||||
|
||||
return userFile;
|
||||
}
|
||||
~~~~
|
||||
@param token A token to impersonate, or 'nullptr' to run as the process identity.
|
||||
*/
|
||||
inline unique_token_reverter impersonate_token(HANDLE token = nullptr)
|
||||
{
|
||||
unique_token_reverter oldToken;
|
||||
THROW_IF_FAILED(impersonate_token_nothrow(token, oldToken));
|
||||
return oldToken;
|
||||
}
|
||||
|
||||
/** Temporarily clears any impersonation on this thread.
|
||||
This method resets the current thread's token to nullptr, indicating that it is not impersonating
|
||||
any user. Useful for elevating to whatever identity a service or higher-privilege process might
|
||||
be capable of running under.
|
||||
~~~~
|
||||
void DeleteFileRetryAsSelf(_In_z_ const wchar_t* filePath)
|
||||
{
|
||||
if (!::DeleteFile(filePath) && (::GetLastError() == ERROR_ACCESS_DENIED))
|
||||
{
|
||||
auto priorToken = wil::run_as_self();
|
||||
TakeOwnershipOfFile(filePath);
|
||||
GrantDeleteAccess(filePath);
|
||||
::DeleteFile(filePath);
|
||||
}
|
||||
}
|
||||
~~~~
|
||||
*/
|
||||
inline unique_token_reverter run_as_self()
|
||||
{
|
||||
return impersonate_token(nullptr);
|
||||
}
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
namespace details
|
||||
{
|
||||
template<size_t AuthorityCount> struct static_sid_t
|
||||
{
|
||||
BYTE Revision;
|
||||
BYTE SubAuthorityCount;
|
||||
SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
|
||||
DWORD SubAuthority[AuthorityCount];
|
||||
|
||||
PSID get()
|
||||
{
|
||||
return reinterpret_cast<PSID>(this);
|
||||
}
|
||||
|
||||
template<size_t other> static_sid_t& operator=(const static_sid_t<other>& source)
|
||||
{
|
||||
static_assert(other <= AuthorityCount, "Cannot assign from a larger static sid to a smaller one");
|
||||
|
||||
if (&this->Revision != &source.Revision)
|
||||
{
|
||||
memcpy(this, &source, sizeof(source));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** Returns a structure containing a Revision 1 SID initialized with the authorities provided
|
||||
Replaces AllocateAndInitializeSid by constructing a structure laid out like a PSID, but
|
||||
returned like a value. The resulting object is suitable for use with any method taking PSID,
|
||||
passed by "&the_sid" or via "the_sid.get()"
|
||||
~~~~
|
||||
// Change the owner of the key to administrators
|
||||
auto systemSid = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS);
|
||||
RETURN_IF_WIN32_ERROR(SetNamedSecurityInfo(keyPath, SE_REGISTRY_KEY, OWNER_SECURITY_INFORMATION, &systemSid, nullptr, nullptr, nullptr));
|
||||
~~~~
|
||||
*/
|
||||
template<typename... Ts> constexpr auto make_static_sid(const SID_IDENTIFIER_AUTHORITY& authority, Ts&&... subAuthorities)
|
||||
{
|
||||
using sid_t = details::static_sid_t<sizeof...(subAuthorities)>;
|
||||
|
||||
static_assert(sizeof...(subAuthorities) <= SID_MAX_SUB_AUTHORITIES, "too many sub authorities");
|
||||
static_assert(offsetof(sid_t, Revision) == offsetof(_SID, Revision), "layout mismatch");
|
||||
static_assert(offsetof(sid_t, SubAuthorityCount) == offsetof(_SID, SubAuthorityCount), "layout mismatch");
|
||||
static_assert(offsetof(sid_t, IdentifierAuthority) == offsetof(_SID, IdentifierAuthority), "layout mismatch");
|
||||
static_assert(offsetof(sid_t, SubAuthority) == offsetof(_SID, SubAuthority), "layout mismatch");
|
||||
|
||||
return sid_t { SID_REVISION, sizeof...(subAuthorities), authority, { static_cast<DWORD>(subAuthorities)... } };
|
||||
}
|
||||
|
||||
//! Variant of static_sid that defaults to the NT authority
|
||||
template<typename... Ts> constexpr auto make_static_nt_sid(Ts&& ... subAuthorities)
|
||||
{
|
||||
return make_static_sid(SECURITY_NT_AUTHORITY, wistd::forward<Ts>(subAuthorities)...);
|
||||
}
|
||||
|
||||
/** Determines whether a specified security identifier (SID) is enabled in an access token.
|
||||
This function determines whether a security identifier, described by a given set of subauthorities, is enabled
|
||||
in the given access token. Note that only up to eight subauthorities can be passed to this function.
|
||||
~~~~
|
||||
bool IsGuest()
|
||||
{
|
||||
return wil::test_token_membership(nullptr, SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS));
|
||||
}
|
||||
~~~~
|
||||
@param result This will be set to true if and only if a security identifier described by the given set of subauthorities is enabled in the given access token.
|
||||
@param token A handle to an access token. The handle must have TOKEN_QUERY access to the token, and must be an impersonation token. If token is nullptr, test_token_membership
|
||||
uses the impersonation token of the calling thread. If the thread is not impersonating, the function duplicates the thread's primary token to create an impersonation token.
|
||||
@param sidAuthority A reference to a SID_IDENTIFIER_AUTHORITY structure. This structure provides the top-level identifier authority value to set in the SID.
|
||||
@param subAuthorities Up to 15 subauthority values to place in the SID (this is a systemwide limit)
|
||||
@return S_OK on success, a FAILED hresult containing the win32 error from creating the SID or querying the token otherwise.
|
||||
*/
|
||||
template<typename... Ts> HRESULT test_token_membership_nothrow(_Out_ bool* result, _In_opt_ HANDLE token,
|
||||
const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities)
|
||||
{
|
||||
*result = false;
|
||||
auto tempSid = make_static_sid(sidAuthority, wistd::forward<Ts>(subAuthorities)...);
|
||||
BOOL isMember;
|
||||
RETURN_IF_WIN32_BOOL_FALSE(CheckTokenMembership(token, &tempSid, &isMember));
|
||||
|
||||
*result = (isMember != FALSE);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/** Determine whether a token represents an app container
|
||||
This method uses the passed in token and emits a boolean indicating that
|
||||
whether TokenIsAppContainer is true.
|
||||
~~~~
|
||||
HRESULT OnlyIfAppContainer()
|
||||
{
|
||||
bool isAppContainer;
|
||||
RETURN_IF_FAILED(wil::get_token_is_app_container_nothrow(nullptr, isAppContainer));
|
||||
RETURN_HR_IF(E_ACCESSDENIED, !isAppContainer);
|
||||
RETURN_HR(...);
|
||||
}
|
||||
~~~~
|
||||
@param token A token to get info about, or 'nullptr' to run as the current thread.
|
||||
*/
|
||||
inline HRESULT get_token_is_app_container_nothrow(_In_opt_ HANDLE token, bool& value)
|
||||
{
|
||||
DWORD isAppContainer = 0;
|
||||
DWORD returnLength = 0;
|
||||
RETURN_IF_WIN32_BOOL_FALSE(::GetTokenInformation(
|
||||
token ? token : GetCurrentThreadEffectiveToken(),
|
||||
TokenIsAppContainer,
|
||||
&isAppContainer,
|
||||
sizeof(isAppContainer),
|
||||
&returnLength));
|
||||
|
||||
value = (isAppContainer != 0);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//! A variant of get_token_is_app_container_nothrow that fails-fast on errors retrieving the token information
|
||||
inline bool get_token_is_app_container_failfast(HANDLE token = nullptr)
|
||||
{
|
||||
bool value = false;
|
||||
FAIL_FAST_IF_FAILED(get_token_is_app_container_nothrow(token, value));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
//! A variant of get_token_is_app_container_nothrow that throws on errors retrieving the token information
|
||||
inline bool get_token_is_app_container(HANDLE token = nullptr)
|
||||
{
|
||||
bool value = false;
|
||||
THROW_IF_FAILED(get_token_is_app_container_nothrow(token, value));
|
||||
|
||||
return value;
|
||||
}
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
template<typename... Ts> bool test_token_membership_failfast(_In_opt_ HANDLE token,
|
||||
const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities)
|
||||
{
|
||||
bool result;
|
||||
FAIL_FAST_IF_FAILED(test_token_membership_nothrow(&result, token, sidAuthority, wistd::forward<Ts>(subAuthorities)...));
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
template<typename... Ts> bool test_token_membership(_In_opt_ HANDLE token, const SID_IDENTIFIER_AUTHORITY& sidAuthority,
|
||||
Ts&&... subAuthorities)
|
||||
{
|
||||
bool result;
|
||||
THROW_IF_FAILED(test_token_membership_nothrow(&result, token, sidAuthority, wistd::forward<Ts>(subAuthorities)...));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
} //namespace wil
|
||||
|
||||
#endif // __WIL_TOKEN_HELPERS_INCLUDED
|
563
Externals/WIL/include/wil/win32_helpers.h
vendored
Normal file
563
Externals/WIL/include/wil/win32_helpers.h
vendored
Normal file
@ -0,0 +1,563 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#ifndef __WIL_WIN32_HELPERS_INCLUDED
|
||||
#define __WIL_WIN32_HELPERS_INCLUDED
|
||||
|
||||
#include <minwindef.h> // FILETIME, HINSTANCE
|
||||
#include <sysinfoapi.h> // GetSystemTimeAsFileTime
|
||||
#include <libloaderapi.h> // GetProcAddress
|
||||
#include <Psapi.h> // GetModuleFileNameExW (macro), K32GetModuleFileNameExW
|
||||
#include <PathCch.h>
|
||||
#include <objbase.h>
|
||||
|
||||
#include "result.h"
|
||||
#include "resource.h"
|
||||
#include "wistd_functional.h"
|
||||
#include "wistd_type_traits.h"
|
||||
|
||||
namespace wil
|
||||
{
|
||||
//! Strictly a function of the file system but this is the value for all known file system, NTFS, FAT.
|
||||
//! CDFs has a limit of 254.
|
||||
size_t const max_path_segment_length = 255;
|
||||
|
||||
//! Character length not including the null, MAX_PATH (260) includes the null.
|
||||
size_t const max_path_length = 259;
|
||||
|
||||
//! 32743 Character length not including the null. This is a system defined limit.
|
||||
//! The 24 is for the expansion of the roots from "C:" to "\Device\HarddiskVolume4"
|
||||
//! It will be 25 when there are more than 9 disks.
|
||||
size_t const max_extended_path_length = 0x7FFF - 24;
|
||||
|
||||
//! For {guid} string form. Includes space for the null terminator.
|
||||
size_t const guid_string_buffer_length = 39;
|
||||
|
||||
//! For {guid} string form. Not including the null terminator.
|
||||
size_t const guid_string_length = 38;
|
||||
|
||||
#pragma region FILETIME helpers
|
||||
// FILETIME duration values. FILETIME is in 100 nanosecond units.
|
||||
namespace filetime_duration
|
||||
{
|
||||
long long const one_millisecond = 10000LL;
|
||||
long long const one_second = 10000000LL;
|
||||
long long const one_minute = 10000000LL * 60; // 600000000 or 600000000LL
|
||||
long long const one_hour = 10000000LL * 60 * 60; // 36000000000 or 36000000000LL
|
||||
long long const one_day = 10000000LL * 60 * 60 * 24; // 864000000000 or 864000000000LL
|
||||
};
|
||||
|
||||
namespace filetime
|
||||
{
|
||||
inline unsigned long long to_int64(const FILETIME &ft)
|
||||
{
|
||||
// Cannot reinterpret_cast FILETIME* to unsigned long long*
|
||||
// due to alignment differences.
|
||||
return (static_cast<unsigned long long>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
|
||||
}
|
||||
|
||||
inline FILETIME from_int64(unsigned long long i64)
|
||||
{
|
||||
static_assert(sizeof(i64) == sizeof(FILETIME), "sizes don't match");
|
||||
static_assert(__alignof(unsigned long long) >= __alignof(FILETIME), "alignment not compatible with type pun");
|
||||
return *reinterpret_cast<FILETIME *>(&i64);
|
||||
}
|
||||
|
||||
inline FILETIME add(_In_ FILETIME const &ft, long long delta)
|
||||
{
|
||||
return from_int64(to_int64(ft) + delta);
|
||||
}
|
||||
|
||||
inline bool is_empty(const FILETIME &ft)
|
||||
{
|
||||
return (ft.dwHighDateTime == 0) && (ft.dwLowDateTime == 0);
|
||||
}
|
||||
|
||||
inline FILETIME get_system_time()
|
||||
{
|
||||
FILETIME ft;
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
return ft;
|
||||
}
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
// Use to adapt Win32 APIs that take a fixed size buffer into forms that return
|
||||
// an allocated buffer. Supports many types of string representation.
|
||||
// See comments below on the expected behavior of the callback.
|
||||
// Adjust stackBufferLength based on typical result sizes to optimize use and
|
||||
// to test the boundary cases.
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT AdaptFixedSizeToAllocatedResult(string_type& result, wistd::function<HRESULT(PWSTR, size_t, size_t*)> callback)
|
||||
{
|
||||
details::string_maker<string_type> maker;
|
||||
|
||||
wchar_t value[stackBufferLength];
|
||||
value[0] = L'\0';
|
||||
size_t valueLengthNeededWithNull{}; // callback returns the number of characters needed including the null terminator.
|
||||
RETURN_IF_FAILED_EXPECTED(callback(value, ARRAYSIZE(value), &valueLengthNeededWithNull));
|
||||
WI_ASSERT(valueLengthNeededWithNull > 0);
|
||||
if (valueLengthNeededWithNull <= ARRAYSIZE(value))
|
||||
{
|
||||
// Success case as described above, make() adds the space for the null.
|
||||
RETURN_IF_FAILED(maker.make(value, valueLengthNeededWithNull - 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Did not fit in the stack allocated buffer, need to do 2 phase construction.
|
||||
// valueLengthNeededWithNull includes the null so subtract that as make() will add space for it.
|
||||
RETURN_IF_FAILED(maker.make(nullptr, valueLengthNeededWithNull - 1));
|
||||
|
||||
size_t secondLength{};
|
||||
RETURN_IF_FAILED(callback(maker.buffer(), valueLengthNeededWithNull, &secondLength));
|
||||
|
||||
// Ensure callback produces consistent result.
|
||||
FAIL_FAST_IF(valueLengthNeededWithNull != secondLength);
|
||||
}
|
||||
result = maker.release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT ExpandEnvironmentStringsW(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT
|
||||
{
|
||||
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
|
||||
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
|
||||
{
|
||||
*valueLengthNeededWithNul = ::ExpandEnvironmentStringsW(input, value, static_cast<DWORD>(valueLength));
|
||||
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
|
||||
/** Searches for a specified file in a specified path using ExpandEnvironmentStringsW(); */
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT SearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, _In_opt_ PCWSTR extension, string_type& result) WI_NOEXCEPT
|
||||
{
|
||||
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
|
||||
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
|
||||
{
|
||||
*valueLengthNeededWithNul = ::SearchPathW(path, fileName, extension, static_cast<DWORD>(valueLength), value, nullptr);
|
||||
|
||||
if (*valueLengthNeededWithNul == 0)
|
||||
{
|
||||
// ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW
|
||||
const HRESULT searchResult = HRESULT_FROM_WIN32(::GetLastError());
|
||||
RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
|
||||
RETURN_IF_FAILED(searchResult);
|
||||
}
|
||||
|
||||
// AdaptFixedSizeToAllocatedResult expects that the length will always include the NUL.
|
||||
// If the result is copied to the buffer, SearchPathW returns the length of copied string, WITHOUT the NUL.
|
||||
// If the buffer is too small to hold the result, SearchPathW returns the length of the required buffer WITH the nul.
|
||||
if (*valueLengthNeededWithNul < valueLength)
|
||||
{
|
||||
(*valueLengthNeededWithNul)++; // It fit, account for the null.
|
||||
}
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
// This function does not work beyond the default stack buffer size (255).
|
||||
// Needs to to retry in a loop similar to wil::GetModuleFileNameExW
|
||||
// These updates and unit tests are tracked by https://github.com/Microsoft/wil/issues/3
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT QueryFullProcessImageNameW(HANDLE processHandle, _In_ DWORD flags, string_type& result) WI_NOEXCEPT
|
||||
{
|
||||
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
|
||||
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
|
||||
{
|
||||
DWORD lengthToUse = static_cast<DWORD>(valueLength);
|
||||
BOOL const success = ::QueryFullProcessImageNameW(processHandle, flags, value, &lengthToUse);
|
||||
RETURN_LAST_ERROR_IF((success == FALSE) && (::GetLastError() != ERROR_INSUFFICIENT_BUFFER));
|
||||
// On both success or insufficient buffer case, add +1 for the null-terminating character
|
||||
*valueLengthNeededWithNul = lengthToUse + 1;
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
/** Expands environment strings and checks path existence with SearchPathW */
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT ExpandEnvAndSearchPath(_In_ PCWSTR input, string_type& result) WI_NOEXCEPT
|
||||
{
|
||||
wil::unique_cotaskmem_string expandedName;
|
||||
RETURN_IF_FAILED((wil::ExpandEnvironmentStringsW<string_type, stackBufferLength>(input, expandedName)));
|
||||
|
||||
// ERROR_FILE_NOT_FOUND is an expected return value for SearchPathW
|
||||
const HRESULT searchResult = (wil::SearchPathW<string_type, stackBufferLength>(nullptr, expandedName.get(), nullptr, result));
|
||||
RETURN_HR_IF_EXPECTED(searchResult, searchResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
|
||||
RETURN_IF_FAILED(searchResult);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Looks up the environment variable 'key' and fails if it is not found.
|
||||
'key' should not have '%' prefix and suffix.
|
||||
Dangerous since environment variable generally are optional. */
|
||||
template <typename string_type>
|
||||
inline HRESULT GetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
|
||||
{
|
||||
return wil::AdaptFixedSizeToAllocatedResult(result,
|
||||
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
|
||||
{
|
||||
// If the function succeeds, the return value is the number of characters stored in the buffer
|
||||
// pointed to by lpBuffer, not including the terminating null character.
|
||||
//
|
||||
// If lpBuffer is not large enough to hold the data, the return value is the buffer size, in
|
||||
// characters, required to hold the string and its terminating null character and the contents of
|
||||
// lpBuffer are undefined.
|
||||
//
|
||||
// If the function fails, the return value is zero. If the specified environment variable was not
|
||||
// found in the environment block, GetLastError returns ERROR_ENVVAR_NOT_FOUND.
|
||||
|
||||
::SetLastError(ERROR_SUCCESS);
|
||||
|
||||
*valueLengthNeededWithNul = ::GetEnvironmentVariableW(key, value, static_cast<DWORD>(valueLength));
|
||||
RETURN_LAST_ERROR_IF_EXPECTED((*valueLengthNeededWithNul == 0) && (::GetLastError() != ERROR_SUCCESS));
|
||||
if (*valueLengthNeededWithNul < valueLength)
|
||||
{
|
||||
(*valueLengthNeededWithNul)++; // It fit, account for the null.
|
||||
}
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
/** Looks up the environment variable 'key' and returns null if it is not found.
|
||||
'key' should not have '%' prefix and suffix. */
|
||||
template <typename string_type>
|
||||
HRESULT TryGetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
|
||||
{
|
||||
const auto hr = wil::GetEnvironmentVariableW<string_type>(key, result);
|
||||
RETURN_HR_IF(hr, FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND)));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/** Retrieves the fully qualified path for the file containing the specified module loaded
|
||||
by a given process. Note GetModuleFileNameExW is a macro.*/
|
||||
template <typename string_type, size_t initialBufferLength = 128>
|
||||
HRESULT GetModuleFileNameExW(_In_opt_ HANDLE process, _In_opt_ HMODULE module, string_type& path)
|
||||
{
|
||||
// initialBufferLength is a template parameter to allow for testing. It creates some waste for
|
||||
// shorter paths, but avoids iteration through the loop in common cases where paths are less
|
||||
// than 128 characters.
|
||||
// wil::max_extended_path_length + 1 (for the null char)
|
||||
// + 1 (to be certain GetModuleFileNameExW didn't truncate)
|
||||
size_t const ensureNoTrucation = (process != nullptr) ? 1 : 0;
|
||||
size_t const maxExtendedPathLengthWithNull = wil::max_extended_path_length + 1 + ensureNoTrucation;
|
||||
|
||||
details::string_maker<string_type> maker;
|
||||
|
||||
for (size_t lengthWithNull = initialBufferLength;
|
||||
lengthWithNull <= maxExtendedPathLengthWithNull;
|
||||
lengthWithNull = (wistd::min)(lengthWithNull * 2, maxExtendedPathLengthWithNull))
|
||||
{
|
||||
// make() adds space for the trailing null
|
||||
RETURN_IF_FAILED(maker.make(nullptr, lengthWithNull - 1));
|
||||
|
||||
DWORD copiedCount;
|
||||
bool copyFailed;
|
||||
bool copySucceededWithNoTruncation;
|
||||
|
||||
if (process != nullptr)
|
||||
{
|
||||
// GetModuleFileNameExW truncates and provides no error or other indication it has done so.
|
||||
// The only way to be sure it didn't truncate is if it didn't need the whole buffer.
|
||||
copiedCount = ::GetModuleFileNameExW(process, module, maker.buffer(), static_cast<DWORD>(lengthWithNull));
|
||||
copyFailed = (0 == copiedCount);
|
||||
copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// In cases of insufficient buffer, GetModuleFileNameW will return a value equal to lengthWithNull
|
||||
// and set the last error to ERROR_INSUFFICIENT_BUFFER.
|
||||
copiedCount = ::GetModuleFileNameW(module, maker.buffer(), static_cast<DWORD>(lengthWithNull));
|
||||
copyFailed = (0 == copiedCount);
|
||||
copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull);
|
||||
}
|
||||
|
||||
if (copyFailed)
|
||||
{
|
||||
RETURN_LAST_ERROR();
|
||||
}
|
||||
else if (copySucceededWithNoTruncation)
|
||||
{
|
||||
path = maker.release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
WI_ASSERT((process != nullptr) || (::GetLastError() == ERROR_INSUFFICIENT_BUFFER));
|
||||
|
||||
if (lengthWithNull == maxExtendedPathLengthWithNull)
|
||||
{
|
||||
// If we've reached this point, there's no point in trying a larger buffer size.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Any path should fit into the maximum max_extended_path_length. If we reached here, something went
|
||||
// terribly wrong.
|
||||
FAIL_FAST();
|
||||
}
|
||||
|
||||
/** Retrieves the fully qualified path for the file that contains the specified module.
|
||||
The module must have been loaded by the current process. The path returned will use the
|
||||
same format that was specified when the module was loaded. Therefore, the path can be a
|
||||
long or short file name, and can have the prefix '\\?\'. */
|
||||
template <typename string_type, size_t initialBufferLength = 128>
|
||||
HRESULT GetModuleFileNameW(HMODULE module, string_type& path)
|
||||
{
|
||||
return wil::GetModuleFileNameExW<string_type, initialBufferLength>(nullptr, module, path);
|
||||
}
|
||||
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT GetSystemDirectoryW(string_type& result) WI_NOEXCEPT
|
||||
{
|
||||
return wil::AdaptFixedSizeToAllocatedResult<string_type, stackBufferLength>(result,
|
||||
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
|
||||
{
|
||||
*valueLengthNeededWithNul = ::GetSystemDirectoryW(value, static_cast<DWORD>(valueLength));
|
||||
RETURN_LAST_ERROR_IF(*valueLengthNeededWithNul == 0);
|
||||
if (*valueLengthNeededWithNul < valueLength)
|
||||
{
|
||||
(*valueLengthNeededWithNul)++; // it fit, account for the null
|
||||
}
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
/** Expands the '%' quoted environment variables in 'input' using ExpandEnvironmentStringsW(); */
|
||||
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
|
||||
string_type ExpandEnvironmentStringsW(_In_ PCWSTR input)
|
||||
{
|
||||
string_type result;
|
||||
THROW_IF_FAILED((wil::ExpandEnvironmentStringsW<string_type, stackBufferLength>(input, result)));
|
||||
return result;
|
||||
}
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
|
||||
/** Searches for a specified file in a specified path using SearchPathW*/
|
||||
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
|
||||
string_type TrySearchPathW(_In_opt_ PCWSTR path, _In_ PCWSTR fileName, PCWSTR _In_opt_ extension)
|
||||
{
|
||||
string_type result;
|
||||
HRESULT searchHR = wil::SearchPathW<string_type, stackBufferLength>(path, fileName, extension, result);
|
||||
THROW_HR_IF(searchHR, FAILED(searchHR) && (searchHR != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)));
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/** Looks up the environment variable 'key' and fails if it is not found.
|
||||
'key' should not have '%' prefix and suffix.
|
||||
Dangerous since environment variable generally are optional. */
|
||||
template <typename string_type = wil::unique_cotaskmem_string>
|
||||
string_type GetEnvironmentVariableW(_In_ PCWSTR key)
|
||||
{
|
||||
string_type result;
|
||||
THROW_IF_FAILED(wil::GetEnvironmentVariableW<string_type>(key, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Looks up the environment variable 'key' and returns null if it is not found.
|
||||
'key' should not have '%' prefix and suffix. */
|
||||
template <typename string_type = wil::unique_cotaskmem_string>
|
||||
string_type TryGetEnvironmentVariableW(_In_ PCWSTR key)
|
||||
{
|
||||
string_type result;
|
||||
THROW_IF_FAILED(wil::TryGetEnvironmentVariableW<string_type>(key, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename string_type = wil::unique_cotaskmem_string>
|
||||
string_type GetModuleFileNameW(HMODULE module)
|
||||
{
|
||||
string_type result;
|
||||
THROW_IF_FAILED(wil::GetModuleFileNameW(module, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename string_type = wil::unique_cotaskmem_string>
|
||||
string_type GetModuleFileNameExW(HANDLE process, HMODULE module)
|
||||
{
|
||||
string_type result;
|
||||
THROW_IF_FAILED(wil::GetModuleFileNameExW(process, module, result));
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/** Retrieve the HINSTANCE for the current DLL or EXE using this symbol that
|
||||
the linker provides for every module. This avoids the need for a global HINSTANCE variable
|
||||
and provides access to this value for static libraries. */
|
||||
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||||
inline HINSTANCE GetModuleInstanceHandle() { return reinterpret_cast<HINSTANCE>(&__ImageBase); }
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
{
|
||||
class init_once_completer
|
||||
{
|
||||
INIT_ONCE& m_once;
|
||||
unsigned long m_flags = INIT_ONCE_INIT_FAILED;
|
||||
public:
|
||||
init_once_completer(_In_ INIT_ONCE& once) : m_once(once)
|
||||
{
|
||||
}
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
|
||||
void success()
|
||||
{
|
||||
m_flags = 0;
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
~init_once_completer()
|
||||
{
|
||||
::InitOnceComplete(&m_once, m_flags, nullptr);
|
||||
}
|
||||
};
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
/** Performs one-time initialization
|
||||
Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked
|
||||
at most once.
|
||||
~~~~
|
||||
INIT_ONCE g_init{};
|
||||
ComPtr<IFoo> g_foo;
|
||||
HRESULT MyMethod()
|
||||
{
|
||||
bool winner = false;
|
||||
RETURN_IF_FAILED(wil::init_once_nothrow(g_init, []
|
||||
{
|
||||
ComPtr<IFoo> foo;
|
||||
RETURN_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo));
|
||||
RETURN_IF_FAILED(foo->Startup());
|
||||
g_foo = foo;
|
||||
}, &winner);
|
||||
if (winner)
|
||||
{
|
||||
RETURN_IF_FAILED(g_foo->Another());
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
~~~~
|
||||
See MSDN for more information on `InitOnceExecuteOnce`.
|
||||
@param initOnce The INIT_ONCE structure to use as context for initialization.
|
||||
@param func A function that will be invoked to perform initialization. If this fails, the init call
|
||||
fails and the once-init is not marked as initialized. A later caller could attempt to
|
||||
initialize it a second time.
|
||||
@param callerCompleted Set to 'true' if this was the call that caused initialization, false otherwise.
|
||||
*/
|
||||
template<typename T> HRESULT init_once_nothrow(_Inout_ INIT_ONCE& initOnce, T func, _Out_opt_ bool* callerCompleted = nullptr) WI_NOEXCEPT
|
||||
{
|
||||
BOOL pending = FALSE;
|
||||
wil::assign_to_opt_param(callerCompleted, false);
|
||||
|
||||
__WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr));
|
||||
|
||||
if (pending)
|
||||
{
|
||||
details::init_once_completer completion(initOnce);
|
||||
__WIL_PRIVATE_RETURN_IF_FAILED(func());
|
||||
completion.success();
|
||||
wil::assign_to_opt_param(callerCompleted, true);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//! Similar to init_once_nothrow, but fails-fast if the initialization step failed. The 'callerComplete' value is
|
||||
//! returned to the caller instead of being an out-parameter.
|
||||
template<typename T> bool init_once_failfast(_Inout_ INIT_ONCE& initOnce, T&& func) WI_NOEXCEPT
|
||||
{
|
||||
bool callerCompleted;
|
||||
|
||||
FAIL_FAST_IF_FAILED(init_once_nothrow(initOnce, wistd::forward<T>(func), &callerCompleted));
|
||||
|
||||
return callerCompleted;
|
||||
};
|
||||
|
||||
//! Returns 'true' if this `init_once` structure has finished initialization, false otherwise.
|
||||
inline bool init_once_initialized(_Inout_ INIT_ONCE& initOnce) WI_NOEXCEPT
|
||||
{
|
||||
BOOL pending = FALSE;
|
||||
return ::InitOnceBeginInitialize(&initOnce, INIT_ONCE_CHECK_ONLY, &pending, nullptr) && !pending;
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
/** Performs one-time initialization
|
||||
Simplifies using the Win32 INIT_ONCE structure to perform one-time initialization. The provided `func` is invoked
|
||||
at most once.
|
||||
~~~~
|
||||
INIT_ONCE g_init{};
|
||||
ComPtr<IFoo> g_foo;
|
||||
void MyMethod()
|
||||
{
|
||||
bool winner = wil::init_once(g_init, []
|
||||
{
|
||||
ComPtr<IFoo> foo;
|
||||
THROW_IF_FAILED(::CoCreateInstance(..., IID_PPV_ARGS(&foo));
|
||||
THROW_IF_FAILED(foo->Startup());
|
||||
g_foo = foo;
|
||||
});
|
||||
if (winner)
|
||||
{
|
||||
THROW_IF_FAILED(g_foo->Another());
|
||||
}
|
||||
}
|
||||
~~~~
|
||||
See MSDN for more information on `InitOnceExecuteOnce`.
|
||||
@param initOnce The INIT_ONCE structure to use as context for initialization.
|
||||
@param func A function that will be invoked to perform initialization. If this fails, the init call
|
||||
fails and the once-init is not marked as initialized. A later caller could attempt to
|
||||
initialize it a second time.
|
||||
@returns 'true' if this was the call that caused initialization, false otherwise.
|
||||
*/
|
||||
template<typename T> bool init_once(_Inout_ INIT_ONCE& initOnce, T func)
|
||||
{
|
||||
BOOL pending = FALSE;
|
||||
|
||||
THROW_IF_WIN32_BOOL_FALSE(::InitOnceBeginInitialize(&initOnce, 0, &pending, nullptr));
|
||||
|
||||
if (pending)
|
||||
{
|
||||
details::init_once_completer completion(initOnce);
|
||||
func();
|
||||
completion.success();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
}
|
||||
|
||||
// Macro for calling GetProcAddress(), with type safety for C++ clients
|
||||
// using the type information from the specified function.
|
||||
// The return value is automatically cast to match the function prototype of the input function.
|
||||
//
|
||||
// Sample usage:
|
||||
//
|
||||
// auto sendMail = GetProcAddressByFunctionDeclaration(hinstMAPI, MAPISendMailW);
|
||||
// if (sendMail)
|
||||
// {
|
||||
// sendMail(0, 0, pmm, MAPI_USE_DEFAULT, 0);
|
||||
// }
|
||||
// Declaration
|
||||
#define GetProcAddressByFunctionDeclaration(hinst, fn) reinterpret_cast<decltype(::fn)*>(GetProcAddress(hinst, #fn))
|
||||
|
||||
#endif // __WIL_WIN32_HELPERS_INCLUDED
|
2231
Externals/WIL/include/wil/winrt.h
vendored
Normal file
2231
Externals/WIL/include/wil/winrt.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
548
Externals/WIL/include/wil/wistd_config.h
vendored
Normal file
548
Externals/WIL/include/wil/wistd_config.h
vendored
Normal file
@ -0,0 +1,548 @@
|
||||
// -*- C++ -*-
|
||||
//===--------------------------- __config ---------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// STL common functionality
|
||||
//
|
||||
// Some aspects of STL are core language concepts that should be used from all C++ code, regardless
|
||||
// of whether exceptions are enabled in the component. Common library code that expects to be used
|
||||
// from exception-free components want these concepts, but including STL headers directly introduces
|
||||
// friction as it requires components not using STL to declare their STL version. Doing so creates
|
||||
// ambiguity around whether STL use is safe in a particular component and implicitly brings in
|
||||
// a long list of headers (including <new>) which can create further ambiguity around throwing new
|
||||
// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has
|
||||
// the potential to create naming conflicts or other implied dependencies.
|
||||
//
|
||||
// To promote the use of these core language concepts outside of STL-based binaries, this file is
|
||||
// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding
|
||||
// "std::" namespace STL functions and types should be preferred over these in code that is bound to
|
||||
// STL. The implementation and naming of all functions are taken directly from STL, instead using
|
||||
// "wistd" (Windows Implementation std) as the namespace.
|
||||
//
|
||||
// Routines in this namespace should always be considered a reflection of the *current* STL implementation
|
||||
// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here.
|
||||
//
|
||||
// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation.
|
||||
// Only code that is not exception-based and libraries that expect to be utilized across both exception
|
||||
// and non-exception based code should utilize this functionality.
|
||||
|
||||
// This header mimics libc++'s '__config' header to the extent necessary to get the wistd::* definitions compiling. Note
|
||||
// that this has a few key differences since libc++'s MSVC compatability is currently not functional and a bit behind
|
||||
|
||||
#ifndef _WISTD_CONFIG_H_
|
||||
#define _WISTD_CONFIG_H_
|
||||
|
||||
// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage
|
||||
#include <stddef.h> // For size_t and other necessary types
|
||||
|
||||
/// @cond
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
# if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
# define __WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef __WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define __WI_GNUC_VER (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||
// The __WI_GNUC_VER_NEW macro better represents the new GCC versioning scheme
|
||||
// introduced in GCC 5.0.
|
||||
# define __WI_GNUC_VER_NEW (__WI_GNUC_VER * 10 + __GNUC_PATCHLEVEL__)
|
||||
#else
|
||||
# define __WI_GNUC_VER 0
|
||||
# define __WI_GNUC_VER_NEW 0
|
||||
#endif
|
||||
|
||||
// _MSVC_LANG is the more accurate way to get the C++ version in MSVC
|
||||
#if defined(_MSVC_LANG) && (_MSVC_LANG > __cplusplus)
|
||||
#define __WI_CPLUSPLUS _MSVC_LANG
|
||||
#else
|
||||
#define __WI_CPLUSPLUS __cplusplus
|
||||
#endif
|
||||
|
||||
#ifndef __WI_LIBCPP_STD_VER
|
||||
# if __WI_CPLUSPLUS <= 201103L
|
||||
# define __WI_LIBCPP_STD_VER 11
|
||||
# elif __WI_CPLUSPLUS <= 201402L
|
||||
# define __WI_LIBCPP_STD_VER 14
|
||||
# elif __WI_CPLUSPLUS <= 201703L
|
||||
# define __WI_LIBCPP_STD_VER 17
|
||||
# else
|
||||
# define __WI_LIBCPP_STD_VER 18 // current year, or date of c++2a ratification
|
||||
# endif
|
||||
#endif // __WI_LIBCPP_STD_VER
|
||||
|
||||
#if __WI_CPLUSPLUS < 201103L
|
||||
#define __WI_LIBCPP_CXX03_LANG
|
||||
#endif
|
||||
|
||||
#if defined(__ELF__)
|
||||
# define __WI_LIBCPP_OBJECT_FORMAT_ELF 1
|
||||
#elif defined(__MACH__)
|
||||
# define __WI_LIBCPP_OBJECT_FORMAT_MACHO 1
|
||||
#elif defined(_WIN32)
|
||||
# define __WI_LIBCPP_OBJECT_FORMAT_COFF 1
|
||||
#elif defined(__wasm__)
|
||||
# define __WI_LIBCPP_OBJECT_FORMAT_WASM 1
|
||||
#else
|
||||
# error Unknown object file format
|
||||
#endif
|
||||
|
||||
#if defined(__clang__)
|
||||
# define __WI_LIBCPP_COMPILER_CLANG
|
||||
#elif defined(__GNUC__)
|
||||
# define __WI_LIBCPP_COMPILER_GCC
|
||||
#elif defined(_MSC_VER)
|
||||
# define __WI_LIBCPP_COMPILER_MSVC
|
||||
#elif defined(__IBMCPP__)
|
||||
# define __WI_LIBCPP_COMPILER_IBM
|
||||
#endif
|
||||
|
||||
// NOTE: MSVC, which is what we primarily target, is severly underrepresented in libc++ and checks such as
|
||||
// __has_feature(...) are always false for MSVC, even when the feature being tested _is_ present in MSVC. Therefore, we
|
||||
// instead modify all checks to be __WI_HAS_FEATURE_IS_UNION, etc., which provides the correct value for MSVC and falls
|
||||
// back to the __has_feature(...), etc. value otherwise. We intentionally leave '__has_feature', etc. undefined for MSVC
|
||||
// so that we don't accidentally use the incorrect behavior
|
||||
#ifndef __WI_LIBCPP_COMPILER_MSVC
|
||||
|
||||
#ifndef __has_feature
|
||||
#define __has_feature(__x) 0
|
||||
#endif
|
||||
|
||||
// '__is_identifier' returns '0' if '__x' is a reserved identifier provided by
|
||||
// the compiler and '1' otherwise.
|
||||
#ifndef __is_identifier
|
||||
#define __is_identifier(__x) 1
|
||||
#endif
|
||||
|
||||
#ifndef __has_cpp_attribute
|
||||
#define __has_cpp_attribute(__x) 0
|
||||
#endif
|
||||
|
||||
#ifndef __has_attribute
|
||||
#define __has_attribute(__x) 0
|
||||
#endif
|
||||
|
||||
#ifndef __has_builtin
|
||||
#define __has_builtin(__x) 0
|
||||
#endif
|
||||
|
||||
#if __has_feature(cxx_alignas)
|
||||
# define __WI_ALIGNAS_TYPE(x) alignas(x)
|
||||
# define __WI_ALIGNAS(x) alignas(x)
|
||||
#else
|
||||
# define __WI_ALIGNAS_TYPE(x) __attribute__((__aligned__(__alignof(x))))
|
||||
# define __WI_ALIGNAS(x) __attribute__((__aligned__(x)))
|
||||
#endif
|
||||
|
||||
#if __has_feature(cxx_explicit_conversions) || defined(__IBMCPP__) || \
|
||||
(!defined(__WI_LIBCPP_CXX03_LANG) && defined(__GNUC__)) // All supported GCC versions
|
||||
# define __WI_LIBCPP_EXPLICIT explicit
|
||||
#else
|
||||
# define __WI_LIBCPP_EXPLICIT
|
||||
#endif
|
||||
|
||||
#if __has_feature(cxx_attributes)
|
||||
# define __WI_LIBCPP_NORETURN [[noreturn]]
|
||||
#else
|
||||
# define __WI_LIBCPP_NORETURN __attribute__ ((noreturn))
|
||||
#endif
|
||||
|
||||
#define __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
||||
#define __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS
|
||||
|
||||
// The __WI_LIBCPP_NODISCARD_ATTRIBUTE should only be used to define other
|
||||
// NODISCARD macros to the correct attribute.
|
||||
#if __has_cpp_attribute(nodiscard)
|
||||
# define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[nodiscard]]
|
||||
#elif defined(__WI_LIBCPP_COMPILER_CLANG) && !defined(__WI_LIBCPP_CXX03_LANG)
|
||||
# define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[clang::warn_unused_result]]
|
||||
#else
|
||||
// We can't use GCC's [[gnu::warn_unused_result]] and
|
||||
// __attribute__((warn_unused_result)), because GCC does not silence them via
|
||||
// (void) cast.
|
||||
# define __WI_LIBCPP_NODISCARD_ATTRIBUTE
|
||||
#endif
|
||||
|
||||
#define __WI_HAS_FEATURE_IS_UNION __has_feature(is_union)
|
||||
#define __WI_HAS_FEATURE_IS_CLASS __has_feature(is_class)
|
||||
#define __WI_HAS_FEATURE_IS_ENUM __has_feature(is_enum)
|
||||
#define __WI_HAS_FEATURE_IS_CONVERTIBLE_TO __has_feature(is_convertible_to)
|
||||
#define __WI_HAS_FEATURE_IS_EMPTY __has_feature(is_empty)
|
||||
#define __WI_HAS_FEATURE_IS_POLYMORPHIC __has_feature(is_polymorphic)
|
||||
#define __WI_HAS_FEATURE_HAS_VIRTUAL_DESTRUCTOR __has_feature(has_virtual_destructor)
|
||||
#define __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS __has_feature(cxx_reference_qualified_functions)
|
||||
#define __WI_HAS_FEATURE_IS_CONSTRUCTIBLE __has_feature(is_constructible)
|
||||
#define __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE __has_feature(is_trivially_constructible)
|
||||
#define __WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE __has_feature(is_trivially_assignable)
|
||||
#define __WI_HAS_FEATURE_HAS_TRIVIAL_DESTRUCTOR __has_feature(has_trivial_destructor)
|
||||
#define __WI_HAS_FEATURE_NOEXCEPT __has_feature(cxx_noexcept)
|
||||
#define __WI_HAS_FEATURE_IS_POD __has_feature(is_pod)
|
||||
#define __WI_HAS_FEATURE_IS_STANDARD_LAYOUT __has_feature(is_standard_layout)
|
||||
#define __WI_HAS_FEATURE_IS_TRIVIALLY_COPYABLE __has_feature(is_trivially_copyable)
|
||||
#define __WI_HAS_FEATURE_IS_TRIVIAL __has_feature(is_trivial)
|
||||
#define __WI_HAS_FEATURE_HAS_TRIVIAL_CONSTRUCTOR __has_feature(has_trivial_constructor) || (__WI_GNUC_VER >= 403)
|
||||
#define __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR __has_feature(has_nothrow_constructor) || (__WI_GNUC_VER >= 403)
|
||||
#define __WI_HAS_FEATURE_HAS_NOTHROW_COPY __has_feature(has_nothrow_copy) || (__WI_GNUC_VER >= 403)
|
||||
#define __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN __has_feature(has_nothrow_assign) || (__WI_GNUC_VER >= 403)
|
||||
|
||||
#if !(__has_feature(cxx_noexcept))
|
||||
#define __WI_LIBCPP_HAS_NO_NOEXCEPT
|
||||
#endif
|
||||
|
||||
#if !__is_identifier(__has_unique_object_representations) || __WI_GNUC_VER >= 700
|
||||
#define __WI_LIBCPP_HAS_UNIQUE_OBJECT_REPRESENTATIONS
|
||||
#endif
|
||||
|
||||
#if !(__has_feature(cxx_variadic_templates))
|
||||
#define __WI_LIBCPP_HAS_NO_VARIADICS
|
||||
#endif
|
||||
|
||||
#if __has_feature(is_literal) || __WI_GNUC_VER >= 407
|
||||
#define __WI_LIBCPP_IS_LITERAL(T) __is_literal(T)
|
||||
#endif
|
||||
|
||||
#if __has_feature(underlying_type) || __WI_GNUC_VER >= 407
|
||||
#define __WI_LIBCPP_UNDERLYING_TYPE(T) __underlying_type(T)
|
||||
#endif
|
||||
|
||||
#if __has_feature(is_final) || __WI_GNUC_VER >= 407
|
||||
#define __WI_LIBCPP_HAS_IS_FINAL
|
||||
#endif
|
||||
|
||||
#if __has_feature(is_base_of) || defined(__GNUC__) && __WI_GNUC_VER >= 403
|
||||
#define __WI_LIBCPP_HAS_IS_BASE_OF
|
||||
#endif
|
||||
|
||||
#if __is_identifier(__is_aggregate) && (__WI_GNUC_VER_NEW < 7001)
|
||||
#define __WI_LIBCPP_HAS_NO_IS_AGGREGATE
|
||||
#endif
|
||||
|
||||
#if !(__has_feature(cxx_rtti)) && !defined(__WI_LIBCPP_NO_RTTI)
|
||||
#define __WI_LIBCPP_NO_RTTI
|
||||
#endif
|
||||
|
||||
#if !(__has_feature(cxx_variable_templates))
|
||||
#define __WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES
|
||||
#endif
|
||||
|
||||
#if !(__has_feature(cxx_relaxed_constexpr))
|
||||
#define __WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR
|
||||
#endif
|
||||
|
||||
#if !__has_builtin(__builtin_addressof) && _GNUC_VER < 700
|
||||
#define __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF
|
||||
#endif
|
||||
|
||||
#if __has_attribute(__no_sanitize__) && !defined(__WI_LIBCPP_COMPILER_GCC)
|
||||
# define __WI_LIBCPP_NO_CFI __attribute__((__no_sanitize__("cfi")))
|
||||
#else
|
||||
# define __WI_LIBCPP_NO_CFI
|
||||
#endif
|
||||
|
||||
#define __WI_LIBCPP_ALWAYS_INLINE __attribute__ ((__always_inline__))
|
||||
|
||||
#if __has_attribute(internal_linkage)
|
||||
# define __WI_LIBCPP_INTERNAL_LINKAGE __attribute__ ((internal_linkage))
|
||||
#else
|
||||
# define __WI_LIBCPP_INTERNAL_LINKAGE __WI_LIBCPP_ALWAYS_INLINE
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
// NOTE: Much of the following assumes a decently recent version of MSVC. Past versions can be supported, but will need
|
||||
// to be updated to contain the proper _MSC_VER check
|
||||
#define __WI_ALIGNAS_TYPE(x) alignas(x)
|
||||
#define __WI_ALIGNAS(x) alignas(x)
|
||||
#define __alignof__ __alignof
|
||||
|
||||
#define __WI_LIBCPP_EXPLICIT explicit
|
||||
#define __WI_LIBCPP_NORETURN [[noreturn]]
|
||||
#define __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __pragma(warning(suppress:26495))
|
||||
#define __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS __pragma(warning(suppress:26439))
|
||||
|
||||
|
||||
#if __WI_LIBCPP_STD_VER > 14
|
||||
#define __WI_LIBCPP_NODISCARD_ATTRIBUTE [[nodiscard]]
|
||||
#else
|
||||
#define __WI_LIBCPP_NODISCARD_ATTRIBUTE _Check_return_
|
||||
#endif
|
||||
|
||||
#define __WI_HAS_FEATURE_IS_UNION 1
|
||||
#define __WI_HAS_FEATURE_IS_CLASS 1
|
||||
#define __WI_HAS_FEATURE_IS_ENUM 1
|
||||
#define __WI_HAS_FEATURE_IS_CONVERTIBLE_TO 1
|
||||
#define __WI_HAS_FEATURE_IS_EMPTY 1
|
||||
#define __WI_HAS_FEATURE_IS_POLYMORPHIC 1
|
||||
#define __WI_HAS_FEATURE_HAS_VIRTUAL_DESTRUCTOR 1
|
||||
#define __WI_LIBCPP_HAS_UNIQUE_OBJECT_REPRESENTATIONS 1
|
||||
#define __WI_HAS_FEATURE_REFERENCE_QUALIFIED_FUNCTIONS 1
|
||||
#define __WI_HAS_FEATURE_IS_CONSTRUCTIBLE 1
|
||||
#define __WI_HAS_FEATURE_IS_TRIVIALLY_CONSTRUCTIBLE 1
|
||||
#define __WI_HAS_FEATURE_IS_TRIVIALLY_ASSIGNABLE 1
|
||||
#define __WI_HAS_FEATURE_HAS_TRIVIAL_DESTRUCTOR 1
|
||||
#define __WI_HAS_FEATURE_NOEXCEPT 1
|
||||
#define __WI_HAS_FEATURE_IS_POD 1
|
||||
#define __WI_HAS_FEATURE_IS_STANDARD_LAYOUT 1
|
||||
#define __WI_HAS_FEATURE_IS_TRIVIALLY_COPYABLE 1
|
||||
#define __WI_HAS_FEATURE_IS_TRIVIAL 1
|
||||
#define __WI_HAS_FEATURE_HAS_TRIVIAL_CONSTRUCTOR 1
|
||||
#define __WI_HAS_FEATURE_HAS_NOTHROW_CONSTRUCTOR 1
|
||||
#define __WI_HAS_FEATURE_HAS_NOTHROW_COPY 1
|
||||
#define __WI_HAS_FEATURE_HAS_NOTHROW_ASSIGN 1
|
||||
#define __WI_HAS_FEATURE_IS_DESTRUCTIBLE 1
|
||||
|
||||
#if !defined(_CPPRTTI) && !defined(__WI_LIBCPP_NO_RTTI)
|
||||
#define __WI_LIBCPP_NO_RTTI
|
||||
#endif
|
||||
|
||||
#define __WI_LIBCPP_IS_LITERAL(T) __is_literal_type(T)
|
||||
#define __WI_LIBCPP_UNDERLYING_TYPE(T) __underlying_type(T)
|
||||
#define __WI_LIBCPP_HAS_IS_FINAL
|
||||
#define __WI_LIBCPP_HAS_IS_BASE_OF
|
||||
|
||||
#if __WI_LIBCPP_STD_VER < 14
|
||||
#define __WI_LIBCPP_HAS_NO_VARIABLE_TEMPLATES
|
||||
#endif
|
||||
|
||||
#define __WI_LIBCPP_HAS_NO_BUILTIN_ADDRESSOF
|
||||
#define __WI_LIBCPP_NO_CFI
|
||||
|
||||
#define __WI_LIBCPP_ALWAYS_INLINE __forceinline
|
||||
#define __WI_LIBCPP_INTERNAL_LINKAGE
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#ifdef __LITTLE_ENDIAN__
|
||||
# if __LITTLE_ENDIAN__
|
||||
# define __WI_LIBCPP_LITTLE_ENDIAN
|
||||
# endif // __LITTLE_ENDIAN__
|
||||
#endif // __LITTLE_ENDIAN__
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
# if __BIG_ENDIAN__
|
||||
# define __WI_LIBCPP_BIG_ENDIAN
|
||||
# endif // __BIG_ENDIAN__
|
||||
#endif // __BIG_ENDIAN__
|
||||
|
||||
#ifdef __BYTE_ORDER__
|
||||
# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
# define __WI_LIBCPP_LITTLE_ENDIAN
|
||||
# elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
# define __WI_LIBCPP_BIG_ENDIAN
|
||||
# endif // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
#endif // __BYTE_ORDER__
|
||||
|
||||
#if !defined(__WI_LIBCPP_LITTLE_ENDIAN) && !defined(__WI_LIBCPP_BIG_ENDIAN)
|
||||
# include <endian.h>
|
||||
# if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
# define __WI_LIBCPP_LITTLE_ENDIAN
|
||||
# elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
# define __WI_LIBCPP_BIG_ENDIAN
|
||||
# else // __BYTE_ORDER == __BIG_ENDIAN
|
||||
# error unable to determine endian
|
||||
# endif
|
||||
#endif // !defined(__WI_LIBCPP_LITTLE_ENDIAN) && !defined(__WI_LIBCPP_BIG_ENDIAN)
|
||||
|
||||
#else // _WIN32
|
||||
|
||||
#define __WI_LIBCPP_LITTLE_ENDIAN
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
#ifdef __WI_LIBCPP_HAS_NO_CONSTEXPR
|
||||
# define __WI_LIBCPP_CONSTEXPR
|
||||
#else
|
||||
# define __WI_LIBCPP_CONSTEXPR constexpr
|
||||
#endif
|
||||
|
||||
#if __WI_LIBCPP_STD_VER > 11 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
|
||||
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX11 constexpr
|
||||
#else
|
||||
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
#endif
|
||||
|
||||
#if __WI_LIBCPP_STD_VER > 14 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
|
||||
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX14 constexpr
|
||||
#else
|
||||
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX14
|
||||
#endif
|
||||
|
||||
#if __WI_LIBCPP_STD_VER > 17 && !defined(__WI_LIBCPP_HAS_NO_CXX14_CONSTEXPR)
|
||||
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX17 constexpr
|
||||
#else
|
||||
# define __WI_LIBCPP_CONSTEXPR_AFTER_CXX17
|
||||
#endif
|
||||
|
||||
#if !defined(__WI_LIBCPP_DISABLE_NODISCARD_AFTER_CXX17) && \
|
||||
(__WI_LIBCPP_STD_VER > 17 || defined(__WI_LIBCPP_ENABLE_NODISCARD))
|
||||
# define __WI_LIBCPP_NODISCARD_AFTER_CXX17 __WI_LIBCPP_NODISCARD_ATTRIBUTE
|
||||
#else
|
||||
# define __WI_LIBCPP_NODISCARD_AFTER_CXX17
|
||||
#endif
|
||||
|
||||
#if __WI_LIBCPP_STD_VER > 14 && defined(__cpp_inline_variables) && (__cpp_inline_variables >= 201606L)
|
||||
# define __WI_LIBCPP_INLINE_VAR inline
|
||||
#else
|
||||
# define __WI_LIBCPP_INLINE_VAR
|
||||
#endif
|
||||
|
||||
#ifdef __WI_LIBCPP_CXX03_LANG
|
||||
#define __WI_LIBCPP_HAS_NO_UNICODE_CHARS
|
||||
#define __WI_LIBCPP_HAS_NO_RVALUE_REFERENCES
|
||||
#endif
|
||||
|
||||
#ifndef __SIZEOF_INT128__
|
||||
#define __WI_LIBCPP_HAS_NO_INT128
|
||||
#endif
|
||||
|
||||
#if !__WI_HAS_FEATURE_NOEXCEPT && !defined(__WI_LIBCPP_HAS_NO_NOEXCEPT)
|
||||
#define __WI_LIBCPP_HAS_NO_NOEXCEPT
|
||||
#endif
|
||||
|
||||
#ifndef __WI_LIBCPP_HAS_NO_NOEXCEPT
|
||||
# define WI_NOEXCEPT noexcept
|
||||
# define __WI_NOEXCEPT_(x) noexcept(x)
|
||||
#else
|
||||
# define WI_NOEXCEPT throw()
|
||||
# define __WI_NOEXCEPT_(x)
|
||||
#endif
|
||||
|
||||
#if defined(__WI_LIBCPP_OBJECT_FORMAT_COFF)
|
||||
#define __WI_LIBCPP_HIDDEN
|
||||
#define __WI_LIBCPP_TEMPLATE_VIS
|
||||
#endif // defined(__WI_LIBCPP_OBJECT_FORMAT_COFF)
|
||||
|
||||
#ifndef __WI_LIBCPP_HIDDEN
|
||||
# if !defined(__WI_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS)
|
||||
# define __WI_LIBCPP_HIDDEN __attribute__ ((__visibility__("hidden")))
|
||||
# else
|
||||
# define __WI_LIBCPP_HIDDEN
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef __WI_LIBCPP_TEMPLATE_VIS
|
||||
# if !defined(__WI_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS) && !defined(__WI_LIBCPP_COMPILER_MSVC)
|
||||
# if __has_attribute(__type_visibility__)
|
||||
# define __WI_LIBCPP_TEMPLATE_VIS __attribute__ ((__type_visibility__("default")))
|
||||
# else
|
||||
# define __WI_LIBCPP_TEMPLATE_VIS __attribute__ ((__visibility__("default")))
|
||||
# endif
|
||||
# else
|
||||
# define __WI_LIBCPP_TEMPLATE_VIS
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_HIDDEN __WI_LIBCPP_INTERNAL_LINKAGE
|
||||
|
||||
namespace wistd // ("Windows Implementation" std)
|
||||
{
|
||||
typedef decltype(__nullptr) nullptr_t;
|
||||
|
||||
template <class _T1, class _T2 = _T1>
|
||||
struct __less
|
||||
{
|
||||
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
|
||||
|
||||
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
bool operator()(const _T1& __x, const _T2& __y) const {return __x < __y;}
|
||||
|
||||
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
bool operator()(const _T2& __x, const _T1& __y) const {return __x < __y;}
|
||||
|
||||
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
bool operator()(const _T2& __x, const _T2& __y) const {return __x < __y;}
|
||||
};
|
||||
|
||||
template <class _T1>
|
||||
struct __less<_T1, _T1>
|
||||
{
|
||||
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
|
||||
};
|
||||
|
||||
template <class _T1>
|
||||
struct __less<const _T1, _T1>
|
||||
{
|
||||
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
|
||||
};
|
||||
|
||||
template <class _T1>
|
||||
struct __less<_T1, const _T1>
|
||||
{
|
||||
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
bool operator()(const _T1& __x, const _T1& __y) const {return __x < __y;}
|
||||
};
|
||||
|
||||
// These are added to wistd to enable use of min/max without having to use the windows.h min/max
|
||||
// macros that some clients might not have access to. Note: the STL versions of these have debug
|
||||
// checking for the less than operator and support for iterators that these implementations lack.
|
||||
// Use the STL versions when you require use of those features.
|
||||
|
||||
// min
|
||||
|
||||
template <class _Tp, class _Compare>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
const _Tp&
|
||||
(min)(const _Tp& __a, const _Tp& __b, _Compare __comp)
|
||||
{
|
||||
return __comp(__b, __a) ? __b : __a;
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
const _Tp&
|
||||
(min)(const _Tp& __a, const _Tp& __b)
|
||||
{
|
||||
return (min)(__a, __b, __less<_Tp>());
|
||||
}
|
||||
|
||||
// max
|
||||
|
||||
template <class _Tp, class _Compare>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
const _Tp&
|
||||
(max)(const _Tp& __a, const _Tp& __b, _Compare __comp)
|
||||
{
|
||||
return __comp(__a, __b) ? __b : __a;
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
const _Tp&
|
||||
(max)(const _Tp& __a, const _Tp& __b)
|
||||
{
|
||||
return (max)(__a, __b, __less<_Tp>());
|
||||
}
|
||||
|
||||
template <class _Arg, class _Result>
|
||||
struct __WI_LIBCPP_TEMPLATE_VIS unary_function
|
||||
{
|
||||
typedef _Arg argument_type;
|
||||
typedef _Result result_type;
|
||||
};
|
||||
|
||||
template <class _Arg1, class _Arg2, class _Result>
|
||||
struct __WI_LIBCPP_TEMPLATE_VIS binary_function
|
||||
{
|
||||
typedef _Arg1 first_argument_type;
|
||||
typedef _Arg2 second_argument_type;
|
||||
typedef _Result result_type;
|
||||
};
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
#endif _WISTD_CONFIG_H_
|
543
Externals/WIL/include/wil/wistd_functional.h
vendored
Normal file
543
Externals/WIL/include/wil/wistd_functional.h
vendored
Normal file
@ -0,0 +1,543 @@
|
||||
// -*- C++ -*-
|
||||
//===------------------------ functional ----------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// STL common functionality
|
||||
//
|
||||
// Some aspects of STL are core language concepts that should be used from all C++ code, regardless
|
||||
// of whether exceptions are enabled in the component. Common library code that expects to be used
|
||||
// from exception-free components want these concepts, but including STL headers directly introduces
|
||||
// friction as it requires components not using STL to declare their STL version. Doing so creates
|
||||
// ambiguity around whether STL use is safe in a particular component and implicitly brings in
|
||||
// a long list of headers (including <new>) which can create further ambiguity around throwing new
|
||||
// support (some routines pulled in may expect it). Secondarily, pulling in these headers also has
|
||||
// the potential to create naming conflicts or other implied dependencies.
|
||||
//
|
||||
// To promote the use of these core language concepts outside of STL-based binaries, this file is
|
||||
// selectively pulling those concepts *directly* from corresponding STL headers. The corresponding
|
||||
// "std::" namespace STL functions and types should be preferred over these in code that is bound to
|
||||
// STL. The implementation and naming of all functions are taken directly from STL, instead using
|
||||
// "wistd" (Windows Implementation std) as the namespace.
|
||||
//
|
||||
// Routines in this namespace should always be considered a reflection of the *current* STL implementation
|
||||
// of those routines. Updates from STL should be taken, but no "bugs" should be fixed here.
|
||||
//
|
||||
// New, exception-based code should not use this namespace, but instead should prefer the std:: implementation.
|
||||
// Only code that is not exception-based and libraries that expect to be utilized across both exception
|
||||
// and non-exception based code should utilize this functionality.
|
||||
|
||||
#ifndef _WISTD_FUNCTIONAL_H_
|
||||
#define _WISTD_FUNCTIONAL_H_
|
||||
|
||||
// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage
|
||||
#include "wistd_memory.h"
|
||||
#include <intrin.h> // For __fastfail
|
||||
#include <new.h> // For placement new
|
||||
|
||||
#if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
#pragma GCC system_header
|
||||
#endif
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4324)
|
||||
|
||||
/// @cond
|
||||
namespace wistd // ("Windows Implementation" std)
|
||||
{
|
||||
// wistd::function
|
||||
//
|
||||
// All of the code below is in direct support of wistd::function. This class is identical to std::function
|
||||
// with the following exceptions:
|
||||
//
|
||||
// 1) It never allocates and is safe to use from exception-free code (custom allocators are not supported)
|
||||
// 2) It's slightly bigger on the stack (64 bytes, rather than 24 for 32bit)
|
||||
// 3) There is an explicit static-assert if a lambda becomes too large to hold in the internal buffer (rather than an allocation)
|
||||
|
||||
template <class _Ret>
|
||||
struct __invoke_void_return_wrapper
|
||||
{
|
||||
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||
template <class ..._Args>
|
||||
static _Ret __call(_Args&&... __args) {
|
||||
return __invoke(wistd::forward<_Args>(__args)...);
|
||||
}
|
||||
#else
|
||||
template <class _Fn>
|
||||
static _Ret __call(_Fn __f) {
|
||||
return __invoke(__f);
|
||||
}
|
||||
|
||||
template <class _Fn, class _A0>
|
||||
static _Ret __call(_Fn __f, _A0& __a0) {
|
||||
return __invoke(__f, __a0);
|
||||
}
|
||||
|
||||
template <class _Fn, class _A0, class _A1>
|
||||
static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1) {
|
||||
return __invoke(__f, __a0, __a1);
|
||||
}
|
||||
|
||||
template <class _Fn, class _A0, class _A1, class _A2>
|
||||
static _Ret __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2){
|
||||
return __invoke(__f, __a0, __a1, __a2);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
template <>
|
||||
struct __invoke_void_return_wrapper<void>
|
||||
{
|
||||
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||
template <class ..._Args>
|
||||
static void __call(_Args&&... __args) {
|
||||
(void)__invoke(wistd::forward<_Args>(__args)...);
|
||||
}
|
||||
#else
|
||||
template <class _Fn>
|
||||
static void __call(_Fn __f) {
|
||||
__invoke(__f);
|
||||
}
|
||||
|
||||
template <class _Fn, class _A0>
|
||||
static void __call(_Fn __f, _A0& __a0) {
|
||||
__invoke(__f, __a0);
|
||||
}
|
||||
|
||||
template <class _Fn, class _A0, class _A1>
|
||||
static void __call(_Fn __f, _A0& __a0, _A1& __a1) {
|
||||
__invoke(__f, __a0, __a1);
|
||||
}
|
||||
|
||||
template <class _Fn, class _A0, class _A1, class _A2>
|
||||
static void __call(_Fn __f, _A0& __a0, _A1& __a1, _A2& __a2) {
|
||||
__invoke(__f, __a0, __a1, __a2);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// FUNCTION
|
||||
//==============================================================================
|
||||
|
||||
// bad_function_call
|
||||
|
||||
__WI_LIBCPP_NORETURN inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||
void __throw_bad_function_call()
|
||||
{
|
||||
__fastfail(7); // FAST_FAIL_FATAL_APP_EXIT
|
||||
}
|
||||
|
||||
template<class _Fp> class __WI_LIBCPP_TEMPLATE_VIS function; // undefined
|
||||
|
||||
namespace __function
|
||||
{
|
||||
|
||||
template<class _Rp>
|
||||
struct __maybe_derive_from_unary_function
|
||||
{
|
||||
};
|
||||
|
||||
template<class _Rp, class _A1>
|
||||
struct __maybe_derive_from_unary_function<_Rp(_A1)>
|
||||
: public unary_function<_A1, _Rp>
|
||||
{
|
||||
};
|
||||
|
||||
template<class _Rp>
|
||||
struct __maybe_derive_from_binary_function
|
||||
{
|
||||
};
|
||||
|
||||
template<class _Rp, class _A1, class _A2>
|
||||
struct __maybe_derive_from_binary_function<_Rp(_A1, _A2)>
|
||||
: public binary_function<_A1, _A2, _Rp>
|
||||
{
|
||||
};
|
||||
|
||||
template <class _Fp>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
bool __not_null(_Fp const&) { return true; }
|
||||
|
||||
template <class _Fp>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
bool __not_null(_Fp* __ptr) { return __ptr; }
|
||||
|
||||
template <class _Ret, class _Class>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
bool __not_null(_Ret _Class::*__ptr) { return __ptr; }
|
||||
|
||||
template <class _Fp>
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
bool __not_null(function<_Fp> const& __f) { return !!__f; }
|
||||
|
||||
} // namespace __function
|
||||
|
||||
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||
|
||||
namespace __function {
|
||||
|
||||
template<class _Fp> class __base;
|
||||
|
||||
template<class _Rp, class ..._ArgTypes>
|
||||
class __base<_Rp(_ArgTypes...)>
|
||||
{
|
||||
__base(const __base&);
|
||||
__base& operator=(const __base&);
|
||||
public:
|
||||
__WI_LIBCPP_INLINE_VISIBILITY __base() {}
|
||||
__WI_LIBCPP_INLINE_VISIBILITY virtual ~__base() {}
|
||||
virtual void __clone(__base*) const = 0;
|
||||
virtual void __move(__base*) = 0;
|
||||
virtual void destroy() WI_NOEXCEPT = 0;
|
||||
virtual _Rp operator()(_ArgTypes&& ...) = 0;
|
||||
};
|
||||
|
||||
template<class _FD, class _FB> class __func;
|
||||
|
||||
template<class _Fp, class _Rp, class ..._ArgTypes>
|
||||
class __func<_Fp, _Rp(_ArgTypes...)>
|
||||
: public __base<_Rp(_ArgTypes...)>
|
||||
{
|
||||
_Fp __f_;
|
||||
public:
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
explicit __func(_Fp&& __f)
|
||||
: __f_(wistd::move(__f)) {}
|
||||
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
explicit __func(const _Fp& __f)
|
||||
: __f_(__f) {}
|
||||
|
||||
virtual void __clone(__base<_Rp(_ArgTypes...)>*) const;
|
||||
virtual void __move(__base<_Rp(_ArgTypes...)>*);
|
||||
virtual void destroy() WI_NOEXCEPT;
|
||||
virtual _Rp operator()(_ArgTypes&& ... __arg);
|
||||
};
|
||||
|
||||
template<class _Fp, class _Rp, class ..._ArgTypes>
|
||||
void
|
||||
__func<_Fp, _Rp(_ArgTypes...)>::__clone(__base<_Rp(_ArgTypes...)>* __p) const
|
||||
{
|
||||
::new (__p) __func(__f_);
|
||||
}
|
||||
|
||||
template<class _Fp, class _Rp, class ..._ArgTypes>
|
||||
void
|
||||
__func<_Fp, _Rp(_ArgTypes...)>::__move(__base<_Rp(_ArgTypes...)>* __p)
|
||||
{
|
||||
::new (__p) __func(wistd::move(__f_));
|
||||
}
|
||||
|
||||
template<class _Fp, class _Rp, class ..._ArgTypes>
|
||||
void
|
||||
__func<_Fp, _Rp(_ArgTypes...)>::destroy() WI_NOEXCEPT
|
||||
{
|
||||
__f_.~_Fp();
|
||||
}
|
||||
|
||||
template<class _Fp, class _Rp, class ..._ArgTypes>
|
||||
_Rp
|
||||
__func<_Fp, _Rp(_ArgTypes...)>::operator()(_ArgTypes&& ... __arg)
|
||||
{
|
||||
typedef __invoke_void_return_wrapper<_Rp> _Invoker;
|
||||
return _Invoker::__call(__f_, wistd::forward<_ArgTypes>(__arg)...);
|
||||
}
|
||||
|
||||
} // __function
|
||||
|
||||
template<class _Rp, class ..._ArgTypes>
|
||||
class __WI_LIBCPP_TEMPLATE_VIS function<_Rp(_ArgTypes...)>
|
||||
: public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>,
|
||||
public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)>
|
||||
{
|
||||
// 'wistd::function' is most similar to 'inplace_function' in that it _only_ permits holding function objects
|
||||
// that can fit within its internal buffer. Therefore, we expand this size to accommodate space for at least 12
|
||||
// pointers (__base vtable takes an additional one).
|
||||
static constexpr size_t __buffer_size = 13 * sizeof(void*);
|
||||
|
||||
typedef __function::__base<_Rp(_ArgTypes...)> __base;
|
||||
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
||||
typename aligned_storage<__buffer_size>::type __buf_;
|
||||
__base* __f_;
|
||||
|
||||
__WI_LIBCPP_NO_CFI static __base *__as_base(void *p) {
|
||||
return reinterpret_cast<__base*>(p);
|
||||
}
|
||||
|
||||
template <class _Fp, bool>
|
||||
struct __callable_imp
|
||||
{
|
||||
static const bool value = is_same<void, _Rp>::value ||
|
||||
is_convertible<typename __invoke_of<_Fp&, _ArgTypes...>::type,
|
||||
_Rp>::value;
|
||||
};
|
||||
|
||||
template <class _Fp>
|
||||
struct __callable_imp<_Fp, false>
|
||||
{
|
||||
static const bool value = false;
|
||||
};
|
||||
|
||||
template <class _Fp>
|
||||
struct __callable
|
||||
{
|
||||
static const bool value = __callable_imp<_Fp, __lazy_and<
|
||||
integral_constant<bool, !is_same<__uncvref_t<_Fp>, function>::value>,
|
||||
__invokable<_Fp&, _ArgTypes...>
|
||||
>::value>::value;
|
||||
};
|
||||
|
||||
template <class _Fp>
|
||||
using _EnableIfCallable = typename enable_if<__callable<_Fp>::value>::type;
|
||||
public:
|
||||
typedef _Rp result_type;
|
||||
|
||||
// construct/copy/destroy:
|
||||
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
||||
function() WI_NOEXCEPT : __f_(0) {}
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
function(nullptr_t) WI_NOEXCEPT : __f_(0) {}
|
||||
function(const function&);
|
||||
function(function&&);
|
||||
template<class _Fp, class = _EnableIfCallable<_Fp>>
|
||||
function(_Fp);
|
||||
|
||||
function& operator=(const function&);
|
||||
function& operator=(function&&);
|
||||
function& operator=(nullptr_t) WI_NOEXCEPT;
|
||||
template<class _Fp, class = _EnableIfCallable<_Fp>>
|
||||
function& operator=(_Fp&&);
|
||||
|
||||
~function();
|
||||
|
||||
// function modifiers:
|
||||
void swap(function&);
|
||||
|
||||
// function capacity:
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
__WI_LIBCPP_EXPLICIT operator bool() const WI_NOEXCEPT {return __f_;}
|
||||
|
||||
// deleted overloads close possible hole in the type system
|
||||
template<class _R2, class... _ArgTypes2>
|
||||
bool operator==(const function<_R2(_ArgTypes2...)>&) const = delete;
|
||||
template<class _R2, class... _ArgTypes2>
|
||||
bool operator!=(const function<_R2(_ArgTypes2...)>&) const = delete;
|
||||
public:
|
||||
// function invocation:
|
||||
_Rp operator()(_ArgTypes...) const;
|
||||
|
||||
// NOTE: type_info is very compiler specific, and on top of that, we're operating in a namespace other than
|
||||
// 'std' so all functions requiring RTTI have been removed
|
||||
};
|
||||
|
||||
template<class _Rp, class ..._ArgTypes>
|
||||
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
||||
function<_Rp(_ArgTypes...)>::function(const function& __f)
|
||||
{
|
||||
if (__f.__f_ == 0)
|
||||
__f_ = 0;
|
||||
else
|
||||
{
|
||||
__f_ = __as_base(&__buf_);
|
||||
__f.__f_->__clone(__f_);
|
||||
}
|
||||
}
|
||||
|
||||
template<class _Rp, class ..._ArgTypes>
|
||||
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS
|
||||
function<_Rp(_ArgTypes...)>::function(function&& __f)
|
||||
{
|
||||
if (__f.__f_ == 0)
|
||||
__f_ = 0;
|
||||
else
|
||||
{
|
||||
__f_ = __as_base(&__buf_);
|
||||
__f.__f_->__move(__f_);
|
||||
__f.__f_->destroy();
|
||||
__f.__f_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template<class _Rp, class ..._ArgTypes>
|
||||
template <class _Fp, class>
|
||||
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
||||
function<_Rp(_ArgTypes...)>::function(_Fp __f)
|
||||
: __f_(0)
|
||||
{
|
||||
if (__function::__not_null(__f))
|
||||
{
|
||||
typedef __function::__func<_Fp, _Rp(_ArgTypes...)> _FF;
|
||||
static_assert(sizeof(_FF) <= sizeof(__buf_),
|
||||
"The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture.");
|
||||
__f_ = ::new((void*)&__buf_) _FF(wistd::move(__f));
|
||||
}
|
||||
}
|
||||
|
||||
template<class _Rp, class ..._ArgTypes>
|
||||
function<_Rp(_ArgTypes...)>&
|
||||
function<_Rp(_ArgTypes...)>::operator=(const function& __f)
|
||||
{
|
||||
*this = nullptr;
|
||||
if (__f.__f_)
|
||||
{
|
||||
__f_ = __as_base(&__buf_);
|
||||
__f.__f_->__clone(__f_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class _Rp, class ..._ArgTypes>
|
||||
function<_Rp(_ArgTypes...)>&
|
||||
function<_Rp(_ArgTypes...)>::operator=(function&& __f)
|
||||
{
|
||||
*this = nullptr;
|
||||
if (__f.__f_)
|
||||
{
|
||||
__f_ = __as_base(&__buf_);
|
||||
__f.__f_->__move(__f_);
|
||||
__f.__f_->destroy();
|
||||
__f.__f_ = 0;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class _Rp, class ..._ArgTypes>
|
||||
function<_Rp(_ArgTypes...)>&
|
||||
function<_Rp(_ArgTypes...)>::operator=(nullptr_t) WI_NOEXCEPT
|
||||
{
|
||||
__base* __t = __f_;
|
||||
__f_ = 0;
|
||||
if (__t)
|
||||
__t->destroy();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class _Rp, class ..._ArgTypes>
|
||||
template <class _Fp, class>
|
||||
function<_Rp(_ArgTypes...)>&
|
||||
function<_Rp(_ArgTypes...)>::operator=(_Fp&& __f)
|
||||
{
|
||||
*this = nullptr;
|
||||
if (__function::__not_null(__f))
|
||||
{
|
||||
typedef __function::__func<typename decay<_Fp>::type, _Rp(_ArgTypes...)> _FF;
|
||||
static_assert(sizeof(_FF) <= sizeof(__buf_),
|
||||
"The sizeof(wistd::function) has grown too large for the reserved buffer (12 pointers). Refactor to reduce size of the capture.");
|
||||
__f_ = ::new((void*)&__buf_) _FF(wistd::move(__f));
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class _Rp, class ..._ArgTypes>
|
||||
function<_Rp(_ArgTypes...)>::~function()
|
||||
{
|
||||
if (__f_)
|
||||
__f_->destroy();
|
||||
}
|
||||
|
||||
template<class _Rp, class ..._ArgTypes>
|
||||
void
|
||||
function<_Rp(_ArgTypes...)>::swap(function& __f)
|
||||
{
|
||||
if (wistd::addressof(__f) == this)
|
||||
return;
|
||||
if (__f_ && __f.__f_)
|
||||
{
|
||||
typename aligned_storage<sizeof(__buf_)>::type __tempbuf;
|
||||
__base* __t = __as_base(&__tempbuf);
|
||||
__f_->__move(__t);
|
||||
__f_->destroy();
|
||||
__f_ = 0;
|
||||
__f.__f_->__move(__as_base(&__buf_));
|
||||
__f.__f_->destroy();
|
||||
__f.__f_ = 0;
|
||||
__f_ = __as_base(&__buf_);
|
||||
__t->__move(__as_base(&__f.__buf_));
|
||||
__t->destroy();
|
||||
__f.__f_ = __as_base(&__f.__buf_);
|
||||
}
|
||||
else if (__f_)
|
||||
{
|
||||
__f_->__move(__as_base(&__f.__buf_));
|
||||
__f_->destroy();
|
||||
__f_ = 0;
|
||||
__f.__f_ = __as_base(&__f.__buf_);
|
||||
}
|
||||
else if (__f.__f_)
|
||||
{
|
||||
__f.__f_->__move(__as_base(&__buf_));
|
||||
__f.__f_->destroy();
|
||||
__f.__f_ = 0;
|
||||
__f_ = __as_base(&__buf_);
|
||||
}
|
||||
}
|
||||
|
||||
template<class _Rp, class ..._ArgTypes>
|
||||
_Rp
|
||||
function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const
|
||||
{
|
||||
if (__f_ == 0)
|
||||
__throw_bad_function_call();
|
||||
return (*__f_)(wistd::forward<_ArgTypes>(__arg)...);
|
||||
}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||
bool
|
||||
operator==(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT {return !__f;}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||
bool
|
||||
operator==(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT {return !__f;}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||
bool
|
||||
operator!=(const function<_Rp(_ArgTypes...)>& __f, nullptr_t) WI_NOEXCEPT {return (bool)__f;}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||
bool
|
||||
operator!=(nullptr_t, const function<_Rp(_ArgTypes...)>& __f) WI_NOEXCEPT {return (bool)__f;}
|
||||
|
||||
// Provide both 'swap_wil' and 'swap' since we now have two ADL scenarios that we need to work
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
swap(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y)
|
||||
{return __x.swap(__y);}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
swap_wil(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y)
|
||||
{return __x.swap(__y);}
|
||||
|
||||
// std::invoke
|
||||
template <class _Fn, class ..._Args>
|
||||
typename __invoke_of<_Fn, _Args...>::type
|
||||
invoke(_Fn&& __f, _Args&&... __args)
|
||||
__WI_NOEXCEPT_((__nothrow_invokable<_Fn, _Args...>::value))
|
||||
{
|
||||
return wistd::__invoke(wistd::forward<_Fn>(__f), wistd::forward<_Args>(__args)...);
|
||||
}
|
||||
|
||||
#else // __WI_LIBCPP_CXX03_LANG
|
||||
|
||||
#error wistd::function and wistd::invoke not implemented for pre-C++11
|
||||
|
||||
#endif
|
||||
}
|
||||
/// @endcond
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
#endif // _WISTD_FUNCTIONAL_H_
|
1038
Externals/WIL/include/wil/wistd_memory.h
vendored
Normal file
1038
Externals/WIL/include/wil/wistd_memory.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
4504
Externals/WIL/include/wil/wistd_type_traits.h
vendored
Normal file
4504
Externals/WIL/include/wil/wistd_type_traits.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
84
Externals/WIL/include/wil/wrl.h
vendored
Normal file
84
Externals/WIL/include/wil/wrl.h
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#ifndef __WIL_WRL_INCLUDED
|
||||
#define __WIL_WRL_INCLUDED
|
||||
|
||||
#include <wrl.h>
|
||||
#include "result.h"
|
||||
#include "common.h" // wistd type_traits helpers
|
||||
|
||||
namespace wil
|
||||
{
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
#pragma region Object construction helpers that throw exceptions
|
||||
|
||||
/** Used to construct a RuntimeClass based object that uses 2 phase construction.
|
||||
Construct a RuntimeClass based object that uses 2 phase construction (by implementing
|
||||
RuntimeClassInitialize() and returning error codes for failures.
|
||||
~~~~
|
||||
// SomeClass uses 2 phase initialization by implementing RuntimeClassInitialize()
|
||||
auto someClass = MakeAndInitializeOrThrow<SomeClass>(L"input", true);
|
||||
~~~~ */
|
||||
|
||||
template <typename T, typename... TArgs>
|
||||
Microsoft::WRL::ComPtr<T> MakeAndInitializeOrThrow(TArgs&&... args)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<T> obj;
|
||||
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<T>(&obj, Microsoft::WRL::Details::Forward<TArgs>(args)...));
|
||||
return obj;
|
||||
}
|
||||
|
||||
/** Used to construct an RuntimeClass based object that uses exceptions in its constructor (and does
|
||||
not require 2 phase construction).
|
||||
~~~~
|
||||
// SomeClass uses exceptions for error handling in its constructor.
|
||||
auto someClass = MakeOrThrow<SomeClass>(L"input", true);
|
||||
~~~~ */
|
||||
|
||||
template <typename T, typename... TArgs>
|
||||
Microsoft::WRL::ComPtr<T> MakeOrThrow(TArgs&&... args)
|
||||
{
|
||||
// This is how you can detect the presence of RuntimeClassInitialize() and find dangerous use.
|
||||
// Unfortunately this produces false positives as all RuntimeClass derived classes have
|
||||
// a RuntimeClassInitialize() method from their base class.
|
||||
// static_assert(!std::is_member_function_pointer<decltype(&T::RuntimeClassInitialize)>::value,
|
||||
// "class has a RuntimeClassInitialize member, use MakeAndInitializeOrThrow instead");
|
||||
auto obj = Microsoft::WRL::Make<T>(Microsoft::WRL::Details::Forward<TArgs>(args)...);
|
||||
THROW_IF_NULL_ALLOC(obj.Get());
|
||||
return obj;
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
/** By default WRL Callback objects are not agile, use this to make an agile one. Replace use of Callback<> with MakeAgileCallback<>.
|
||||
Will return null on failure, translate that into E_OUTOFMEMORY using XXX_IF_NULL_ALLOC()
|
||||
from wil\result.h to test the result. */
|
||||
template<typename TDelegateInterface, typename ...Args>
|
||||
::Microsoft::WRL::ComPtr<TDelegateInterface> MakeAgileCallbackNoThrow(Args&&... args) WI_NOEXCEPT
|
||||
{
|
||||
using namespace Microsoft::WRL;
|
||||
return Callback<Implements<RuntimeClassFlags<ClassicCom>, TDelegateInterface, FtmBase>>(wistd::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
template<typename TDelegateInterface, typename ...Args>
|
||||
::Microsoft::WRL::ComPtr<TDelegateInterface> MakeAgileCallback(Args&&... args)
|
||||
{
|
||||
auto result = MakeAgileCallbackNoThrow<TDelegateInterface, Args...>(wistd::forward<Args>(args)...);
|
||||
THROW_IF_NULL_ALLOC(result);
|
||||
return result;
|
||||
}
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
} // namespace wil
|
||||
|
||||
#endif // __WIL_WRL_INCLUDED
|
Reference in New Issue
Block a user