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