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