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