| //********************************************************* | |||||
| // | |||||
| // 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 |
| //********************************************************* | |||||
| // | |||||
| // 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 |
| //********************************************************* | |||||
| // | |||||
| // 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 |
| //********************************************************* | |||||
| // | |||||
| // 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 |
| //********************************************************* | |||||
| // | |||||
| // 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 |
| //********************************************************* | |||||
| // | |||||
| // 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 |
| //********************************************************* | |||||
| // | |||||
| // 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 |
| //********************************************************* | |||||
| // | |||||
| // 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 |
| //********************************************************* | |||||
| // | |||||
| // 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 |
| //********************************************************* | |||||
| // | |||||
| // 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 |
| // -*- 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_ |
| // -*- 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_ |
| //********************************************************* | |||||
| // | |||||
| // 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 |
| Private-Include: external/spdlog/include | |||||
| Private-Include: external/spdlog/include | |||||
| Private-Include: external/wil/include |
| imports: | imports: | ||||
| spdlog: external/spdlog | spdlog: external/spdlog | ||||
| taywee-args: external/taywee-args | taywee-args: external/taywee-args | ||||
| wil: external/wil | |||||
| git module spdlog: | git module spdlog: | ||||
| url: https://github.com/gabime/spdlog.git | url: https://github.com/gabime/spdlog.git | ||||
| move: | move: | ||||
| args.hxx: include/args.hxx | args.hxx: include/args.hxx | ||||
| pick: include | pick: include | ||||
| git module wil: | |||||
| url: https://github.com/microsoft/wil.git | |||||
| rev: 6de0b3e6c1bad18feaedd2589d452d66e7fc5600 | |||||
| pick: include |
| #include <dds/toolchain.hpp> | #include <dds/toolchain.hpp> | ||||
| #include <algorithm> | #include <algorithm> | ||||
| #include <sstream> | |||||
| #include <iomanip> | #include <iomanip> | ||||
| #include <iostream> | #include <iostream> | ||||
| #include <mutex> | #include <mutex> | ||||
| throw compile_failure("Compilation failed."); | throw compile_failure("Compilation failed."); | ||||
| } | } | ||||
| // MSVC prints the filename of the source file. Dunno why, but they do. | |||||
| if (compile_res.output.find(spec.source_path.filename().string() + "\r\n") == 0) { | |||||
| compile_res.output.erase(0, spec.source_path.filename().string().length() + 2); | |||||
| } | |||||
| if (!compile_res.output.empty()) { | if (!compile_res.output.empty()) { | ||||
| spdlog::warn("While compiling file {}:\n{}", spec.source_path.string(), compile_res.output); | spdlog::warn("While compiling file {}:\n{}", spec.source_path.string(), compile_res.output); | ||||
| } | } | ||||
| std::unique_lock lk{mut}; | std::unique_lock lk{mut}; | ||||
| std::vector<std::thread> threads; | std::vector<std::thread> threads; | ||||
| std::generate_n(std::back_inserter(threads), std::thread::hardware_concurrency() + 2, [&] { | |||||
| int njobs = params.parallel_jobs; | |||||
| if (njobs < 1) { | |||||
| njobs = std::thread::hardware_concurrency() + 2; | |||||
| } | |||||
| std::generate_n(std::back_inserter(threads), njobs, [&] { | |||||
| return std::thread(compile_one); | return std::thread(compile_one); | ||||
| }); | }); | ||||
| spdlog::info("Parallel compile with {} threads", threads.size()); | spdlog::info("Parallel compile with {} threads", threads.size()); |
| bool do_export = false; | bool do_export = false; | ||||
| bool build_tests = false; | bool build_tests = false; | ||||
| bool enable_warnings = false; | bool enable_warnings = false; | ||||
| int parallel_jobs = 0; | |||||
| }; | }; | ||||
| void build(const build_params&, const library_manifest& man); | void build(const build_params&, const library_manifest& man); |
| "export_name", | "export_name", | ||||
| "Set the name of the export", | "Set the name of the export", | ||||
| {"export-name", 'n'}, | {"export-name", 'n'}, | ||||
| dds::fs::current_path().filename()}; | |||||
| dds::fs::current_path().filename().string()}; | |||||
| path_flag tc_filepath{cmd, | path_flag tc_filepath{cmd, | ||||
| "toolchain_file", | "toolchain_file", | ||||
| args::Flag enable_warnings{cmd, | args::Flag enable_warnings{cmd, | ||||
| "enable_warnings", | "enable_warnings", | ||||
| "Enable compiler warnings", | "Enable compiler warnings", | ||||
| {"--warnings", 'W'}}; | |||||
| {"warnings", 'W'}}; | |||||
| args::ValueFlag<int> num_jobs{ cmd, | |||||
| "jobs", | |||||
| "Set the number of parallel jobs when compiling files", | |||||
| { "jobs", 'j' }, | |||||
| 0 }; | |||||
| int run() { | int run() { | ||||
| dds::build_params params; | dds::build_params params; | ||||
| params.do_export = export_.Get(); | params.do_export = export_.Get(); | ||||
| params.build_tests = build_tests.Get(); | params.build_tests = build_tests.Get(); | ||||
| params.enable_warnings = enable_warnings.Get(); | params.enable_warnings = enable_warnings.Get(); | ||||
| params.parallel_jobs = num_jobs.Get(); | |||||
| dds::library_manifest man; | dds::library_manifest man; | ||||
| const auto man_filepath = params.root / "manifest.dds"; | const auto man_filepath = params.root / "manifest.dds"; | ||||
| if (exists(man_filepath)) { | if (exists(man_filepath)) { |
| #include <dds/util.hpp> | #include <dds/util.hpp> | ||||
| #include <fstream> | #include <fstream> | ||||
| #include <cctype> | |||||
| namespace fs = std::filesystem; | namespace fs = std::filesystem; | ||||
| namespace { | namespace { | ||||
| std::string_view sview(std::string_view::const_iterator beg, std::string_view::const_iterator end) { | std::string_view sview(std::string_view::const_iterator beg, std::string_view::const_iterator end) { | ||||
| return std::string_view{beg, static_cast<std::size_t>(std::distance(beg, end))}; | |||||
| return std::string_view(&*beg, static_cast<std::size_t>(std::distance(beg, end))); | |||||
| } | } | ||||
| std::string_view trim(std::string_view s) { | std::string_view trim(std::string_view s) { |
| auto& key() const noexcept { return _key; } | auto& key() const noexcept { return _key; } | ||||
| auto& value() const noexcept { return _value; } | auto& value() const noexcept { return _value; } | ||||
| auto tie() const noexcept { return std::tie(key(), value()); } | |||||
| }; | }; | ||||
| struct kv_pair_iterator { | struct kv_pair_iterator { |
| #ifndef _WIN32 | |||||
| #include "./proc.hpp" | #include "./proc.hpp" | ||||
| #include <poll.h> | #include <poll.h> | ||||
| res.signal = WTERMSIG(status); | res.signal = WTERMSIG(status); | ||||
| } | } | ||||
| return res; | return res; | ||||
| } | |||||
| } | |||||
| #endif // _WIN32 |
| #ifdef _WIN32 | |||||
| #include "./proc.hpp" | |||||
| #include <dds/logging.hpp> | |||||
| #include <wil/resource.h> | |||||
| #include <windows.h> | |||||
| #include <cassert> | |||||
| #include <iomanip> | |||||
| #include <sstream> | |||||
| #include <stdexcept> | |||||
| using namespace dds; | |||||
| namespace { | |||||
| [[noreturn]] void throw_system_error(const char* what) { | |||||
| throw std::system_error(std::error_code(::GetLastError(), std::system_category()), what); | |||||
| } | |||||
| std::string concat_args(const std::vector<std::string>& cmd) { | |||||
| std::stringstream strm; | |||||
| for (const auto& arg : cmd) { | |||||
| strm << arg << ' '; | |||||
| } | |||||
| return strm.str(); | |||||
| } | |||||
| } // namespace | |||||
| proc_result dds::run_proc(const std::vector<std::string>& cmd) { | |||||
| auto cmd_str = concat_args(cmd); | |||||
| ::SECURITY_ATTRIBUTES security = {}; | |||||
| security.bInheritHandle = TRUE; | |||||
| security.nLength = sizeof security; | |||||
| security.lpSecurityDescriptor = nullptr; | |||||
| wil::unique_hfile reader; | |||||
| wil::unique_hfile writer; | |||||
| auto okay = ::CreatePipe(&reader, &writer, &security, 0); | |||||
| if (!okay) { | |||||
| throw_system_error("Failed to create a stdio pipe"); | |||||
| } | |||||
| ::SetHandleInformation(reader.get(), HANDLE_FLAG_INHERIT, 0); | |||||
| wil::unique_process_information proc_info; | |||||
| ::STARTUPINFOA startup_info = {}; | |||||
| ::RtlSecureZeroMemory(&startup_info, sizeof startup_info); | |||||
| startup_info.hStdOutput = startup_info.hStdError = writer.get(); | |||||
| startup_info.dwFlags = STARTF_USESTDHANDLES; | |||||
| startup_info.cb = sizeof startup_info; | |||||
| okay = ::CreateProcessA(nullptr, // cmd[0].data(), | |||||
| cmd_str.data(), | |||||
| nullptr, | |||||
| nullptr, | |||||
| true, | |||||
| 0, | |||||
| nullptr, | |||||
| nullptr, | |||||
| &startup_info, | |||||
| &proc_info); | |||||
| if (!okay) { | |||||
| throw_system_error("Failed to spawn a child process"); | |||||
| } | |||||
| writer.reset(); | |||||
| std::string output; | |||||
| while (true) { | |||||
| const int buffer_size = 256; | |||||
| char buffer[buffer_size]; | |||||
| DWORD nread = 0; | |||||
| okay = ::ReadFile(reader.get(), buffer, buffer_size, &nread, nullptr); | |||||
| if (!okay && ::GetLastError() != ERROR_BROKEN_PIPE) { | |||||
| throw_system_error("Failed while reading from the stdio pipe"); | |||||
| } | |||||
| output.append(buffer, buffer + nread); | |||||
| if (nread == 0) { | |||||
| break; | |||||
| } | |||||
| } | |||||
| ::WaitForSingleObject(proc_info.hProcess, INFINITE); | |||||
| DWORD rc = 0; | |||||
| okay = ::GetExitCodeProcess(proc_info.hProcess, &rc); | |||||
| if (!okay || rc == STILL_ACTIVE) { | |||||
| throw_system_error("Failed reading exit code of process"); | |||||
| } | |||||
| proc_result res; | |||||
| res.retc = rc; | |||||
| res.output = std::move(output); | |||||
| return res; | |||||
| } | |||||
| #endif // _WIN32 |
| vector<string> cmd; | vector<string> cmd; | ||||
| for (auto& arg : _archive_template) { | for (auto& arg : _archive_template) { | ||||
| if (arg == "<OBJECTS>") { | if (arg == "<OBJECTS>") { | ||||
| extend(cmd, spec.input_files); | |||||
| std::transform(spec.input_files.begin(), spec.input_files.end(), std::back_inserter(cmd), [](auto&& p) { | |||||
| return p.string(); | |||||
| }); | |||||
| } else { | } else { | ||||
| cmd.push_back(replace(arg, "<ARCHIVE>", spec.out_path.string())); | cmd.push_back(replace(arg, "<ARCHIVE>", spec.out_path.string())); | ||||
| } | } |
| #include <filesystem> | #include <filesystem> | ||||
| #include <fstream> | #include <fstream> | ||||
| #include <sstream> | |||||
| std::fstream dds::open(const fs::path& filepath, std::ios::openmode mode, std::error_code& ec) { | std::fstream dds::open(const fs::path& filepath, std::ios::openmode mode, std::error_code& ec) { | ||||
| std::fstream ret; | std::fstream ret; | ||||
| try { | try { | ||||
| ret.open(filepath.string(), mode); | ret.open(filepath.string(), mode); | ||||
| } catch (const std::ios::failure& e) { | |||||
| } catch (const std::ios::failure&) { | |||||
| ec = std::error_code(errno, std::system_category()); | ec = std::error_code(errno, std::system_category()); | ||||
| } | } | ||||
| return ret; | return ret; |
| Include-Template: /I<PATH> | |||||
| Define-Template: /D<DEF> | |||||
| Compile-C-Template: cl.exe /nologo <FLAGS> /c <FILE> /Fo<OUT> | |||||
| Compile-C++-Template: cl.exe /nologo <FLAGS> /std:c++latest /EHsc /c <FILE> /Fo<OUT> | |||||
| Create-Archive-Template: lib /nologo /OUT:<ARCHIVE> <OBJECTS> | |||||
| Archive-Suffix: .lib |