Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -75,27 +75,7 @@
#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
@@ -107,73 +87,6 @@ WI_ODR_PRAGMA("WIL_KERNEL_MODE", "0")
#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.
//! @{
@@ -327,8 +240,96 @@ Three exception modes are available:

//! @} // 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
#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 "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

#if (defined(_CPPUNWIND) || defined(__EXCEPTIONS)) && !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 or __EXCEPTIONS 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

#define __R_ENABLE_IF_IS_CLASS(ptrType) wistd::enable_if_t<wistd::is_class<ptrType>::value, void*> = nullptr
#define __R_ENABLE_IF_IS_NOT_CLASS(ptrType) wistd::enable_if_t<!wistd::is_class<ptrType>::value, void*> = nullptr

//! @defgroup bitwise Bitwise Inspection and Manipulation
//! Bitwise helpers to improve readability and reduce the error rate of bitwise operations.
@@ -612,10 +613,10 @@ namespace wil
}

template <>
_Post_satisfies_(return == !!val)
_Post_satisfies_(return == (val != 0))
__forceinline constexpr bool verify_bool<unsigned char>(unsigned char val)
{
return !!val;
return (val != 0);
}

/** Verify that `val` is a Win32 BOOL value.
@@ -651,16 +652,62 @@ namespace wil
~~~~
RETURN_HR_IF(static_cast<HRESULT>(UIA_E_NOTSUPPORTED), (patternId != UIA_DragPatternId));
~~~~
@param val The HRESULT returning expression
@param hr 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
// Note: Written in terms of 'long' 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;
}

/** Verify that `status` is an NTSTATUS value.
Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is the
underlying typedef behind NTSTATUS.
//!
Note that occasionally you might run into an NTSTATUS which is directly defined with a #define, such as:
~~~~
#define STATUS_NOT_SUPPORTED 0x1
~~~~
Though this looks like an `NTSTATUS`, 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 `NTSTATUS` constants are defined in ntstatus.h:
~~~~
#define STATUS_NOT_SUPPORTED ((NTSTATUS)0xC00000BBL)
~~~~
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_ntstatus`, for example:
~~~~
NT_RETURN_IF_FALSE(static_cast<NTSTATUS>(STATUS_NOT_SUPPORTED), (dispatch->Version == HKE_V1_0));
~~~~
@param status The NTSTATUS returning expression
@return An NTSTATUS representing the evaluation of `val`. */
template <typename T>
_Post_satisfies_(return == status)
inline long verify_ntstatus(T status)
{
// Note: Written in terms of 'long' as NTSTATUS is actually: typedef _Return_type_success_(return >= 0) long NTSTATUS
static_assert(wistd::is_same<T, long>::value, "Wrong Type: NTSTATUS expected");
return status;
}

/** Verify that `error` is a Win32 error code.
Other types will generate an intentional compilation error. Note that this will accept any `long` value as that is
the underlying type used for WIN32 error codes, as well as any `DWORD` (`unsigned long`) value since this is the type
commonly used when manipulating Win32 error codes.
@param error The Win32 error code returning expression
@return An Win32 error code representing the evaluation of `error`. */
template <typename T>
_Post_satisfies_(return == error)
inline T verify_win32(T error)
{
// Note: Win32 error code are defined as 'long' (#define ERROR_SUCCESS 0L), but are more frequently used as DWORD (unsigned long).
// This accept both types.
static_assert(wistd::is_same<T, long>::value || wistd::is_same<T, unsigned long>::value, "Wrong Type: Win32 error code (long / unsigned long) expected");
return error;
}
/// @} // end type validation routines

/// @cond
@@ -706,31 +753,31 @@ namespace wil
template <>
struct variable_size<1>
{
typedef unsigned char type;
using type = unsigned char;
};

template <>
struct variable_size<2>
{
typedef unsigned short type;
using type = unsigned short;
};

template <>
struct variable_size<4>
{
typedef unsigned long type;
using type = unsigned long;
};

template <>
struct variable_size<8>
{
typedef unsigned long long type;
using type = unsigned long long;
};

template <typename T>
struct variable_size_mapping
{
typedef typename variable_size<sizeof(T)>::type type;
using type = typename variable_size<sizeof(T)>::type;
};
} // details
/// @endcond
@@ -739,6 +786,10 @@ namespace wil
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;

//! Declares a name that intentionally hides a name from an outer scope.
//! Use this to prevent accidental use of a parameter or lambda captured variable.
using hide_name = void(struct hidden_name);
} // wil

#pragma warning(pop)
@@ -14,6 +14,7 @@
#include "common.h"
#include <windows.h>
#include <unknwn.h>
#include <inspectable.h>
#include <hstring.h>

// WIL and C++/WinRT use two different exception types for communicating HRESULT failures. Thus, both libraries need to
@@ -27,18 +28,43 @@
namespace wil::details
{
// Since the C++/WinRT version macro is a string...
inline constexpr int major_version_from_string(const char* versionString)
// For example: "2.0.221104.6"
inline constexpr int version_from_string(const char* versionString)
{
int result = 0;
auto str = versionString;
while ((*str >= '0') && (*str <= '9'))
while ((*versionString >= '0') && (*versionString <= '9'))
{
result = result * 10 + (*str - '0');
++str;
result = result * 10 + (*versionString - '0');
++versionString;
}

return result;
}

inline constexpr int major_version_from_string(const char* versionString)
{
return version_from_string(versionString);
}

inline constexpr int minor_version_from_string(const char* versionString)
{
int dotCount = 0;
while ((*versionString != '\0'))
{
if (*versionString == '.')
{
++dotCount;
}

++versionString;
if (dotCount == 2)
{
return version_from_string(versionString);
}
}

return 0;
}
}
/// @endcond

@@ -74,6 +100,9 @@ static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2,
// use it unless the version of C++/WinRT is high enough
extern std::int32_t(__stdcall* winrt_to_hresult_handler)(void*) noexcept;

// The same is true with this function pointer as well, except that the version must be 2.X or higher.
extern void(__stdcall* winrt_throw_hresult_handler)(uint32_t, char const*, char const*, void*, winrt::hresult const) noexcept;

/// @cond
namespace wil::details
{
@@ -108,7 +137,7 @@ namespace wil::details
catch (const winrt::hresult_error& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.code().value;
return exception.to_abi();
}
catch (const std::bad_alloc& exception)
{
@@ -149,7 +178,7 @@ namespace wil::details
catch (const winrt::hresult_error& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.code().value;
return exception.to_abi();
}
catch (const std::bad_alloc& exception)
{
@@ -192,16 +221,28 @@ namespace wil
return static_cast<std::int32_t>(details::ReportFailure_CaughtException<FailureType::Return>(__R_DIAGNOSTICS_RA(DiagnosticsInfo{}, returnAddress)));
}

inline void __stdcall winrt_throw_hresult(uint32_t lineNumber, char const* fileName, char const* functionName, void* returnAddress, winrt::hresult const result) noexcept
{
void* callerReturnAddress{nullptr}; PCSTR code{nullptr};
wil::details::ReportFailure_Hr<FailureType::Log>(__R_FN_CALL_FULL __R_COMMA result);
}

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;

if constexpr (details::minor_version_from_string(CPPWINRT_VERSION) >= 210122)
{
WI_ASSERT(winrt_throw_hresult_handler == nullptr);
winrt_throw_hresult_handler = winrt_throw_hresult;
}
}
}

/// @cond
namespace details
{
@@ -236,6 +277,11 @@ namespace wil
return static_cast<HSTRING>(winrt::get_abi(object));
}

inline auto str_raw_ptr(const winrt::hstring& str) noexcept
{
return str.c_str();
}

template <typename T>
auto put_abi(T& object) noexcept
{
@@ -246,6 +292,117 @@ namespace wil
{
return reinterpret_cast<HSTRING*>(winrt::put_abi(object));
}

inline ::IUnknown* com_raw_ptr(const winrt::Windows::Foundation::IUnknown& ptr) noexcept
{
return static_cast<::IUnknown*>(winrt::get_abi(ptr));
}

// Needed to power wil::cx_object_from_abi that requires IInspectable
inline ::IInspectable* com_raw_ptr(const winrt::Windows::Foundation::IInspectable& ptr) noexcept
{
return static_cast<::IInspectable*>(winrt::get_abi(ptr));
}

// Taken from the docs.microsoft.com article
template <typename T>
T convert_from_abi(::IUnknown* from)
{
T to{ nullptr }; // `T` is a projected type.
winrt::check_hresult(from->QueryInterface(winrt::guid_of<T>(), winrt::put_abi(to)));
return to;
}

// For obtaining an object from an interop method on the factory. Example:
// winrt::InputPane inputPane = wil::capture_interop<winrt::InputPane>(&IInputPaneInterop::GetForWindow, hwnd);
// If the method produces something different from the factory type:
// winrt::IAsyncAction action = wil::capture_interop<winrt::IAsyncAction, winrt::AccountsSettingsPane>(&IAccountsSettingsPaneInterop::ShowAddAccountForWindow, hwnd);
template<typename WinRTResult, typename WinRTFactory = WinRTResult, typename Interface, typename... InterfaceArgs, typename... Args>
auto capture_interop(HRESULT(__stdcall Interface::* method)(InterfaceArgs...), Args&&... args)
{
auto interop = winrt::get_activation_factory<WinRTFactory, Interface>();
return winrt::capture<WinRTResult>(interop, method, std::forward<Args>(args)...);
}

// For obtaining an object from an interop method on an instance. Example:
// winrt::UserActivitySession session = wil::capture_interop<winrt::UserActivitySession>(activity, &IUserActivityInterop::CreateSessionForWindow, hwnd);
template<typename WinRTResult, typename Interface, typename... InterfaceArgs, typename... Args>
auto capture_interop(winrt::Windows::Foundation::IUnknown const& o, HRESULT(__stdcall Interface::* method)(InterfaceArgs...), Args&&... args)
{
return winrt::capture<WinRTResult>(o.as<Interface>(), method, std::forward<Args>(args)...);
}

/** Holds a reference to the host C++/WinRT module to prevent it from being unloaded.
Normally, this is done by being in an IAsyncOperation coroutine or by holding a strong
reference to a C++/WinRT object hosted in the same module, but if you have neither,
you will need to hold a reference explicitly. For the WRL equivalent, see wrl_module_reference.
This can be used as a base, which permits EBO:
~~~~
struct NonWinrtObject : wil::winrt_module_reference
{
int value;
};
// DLL will not be unloaded as long as NonWinrtObject is still alive.
auto p = std::make_unique<NonWinrtObject>();
~~~~
Or it can be used as a member (with [[no_unique_address]] to avoid
occupying any memory):
~~~~
struct NonWinrtObject
{
int value;
[[no_unique_address]] wil::winrt_module_reference module_ref;
};
// DLL will not be unloaded as long as NonWinrtObject is still alive.
auto p = std::make_unique<NonWinrtObject>();
~~~~
If using it to prevent the host DLL from unloading while a thread
or threadpool work item is still running, create the object before
starting the thread, and pass it to the thread. This avoids a race
condition where the host DLL could get unloaded before the thread starts.
~~~~
std::thread([module_ref = wil::winrt_module_reference()]() { do_background_work(); });
// Don't do this (race condition)
std::thread([]() { wil::winrt_module_reference module_ref; do_background_work(); }); // WRONG
~~~~
Also useful in coroutines that neither capture DLL-hosted COM objects, nor are themselves
DLL-hosted COM objects. (If the coroutine returns IAsyncAction or captures a get_strong()
of its containing WinRT class, then the IAsyncAction or strong reference will itself keep
a strong reference to the host module.)
~~~~
winrt::fire_and_forget ContinueBackgroundWork()
{
// prevent DLL from unloading while we are running on a background thread.
// Do this before switching to the background thread.
wil::winrt_module_reference module_ref;
co_await winrt::resume_background();
do_background_work();
};
~~~~
*/
struct [[nodiscard]] winrt_module_reference
{
winrt_module_reference()
{
++winrt::get_module_lock();
}

winrt_module_reference(winrt_module_reference const&) : winrt_module_reference() {}

~winrt_module_reference()
{
--winrt::get_module_lock();
}
};
}

#endif // __WIL_CPPWINRT_INCLUDED
@@ -0,0 +1,352 @@
//*********************************************************
//
// 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_HELPERS_DEFINED
#define __WIL_CPPWINRT_HELPERS_DEFINED

/// @cond
namespace wil::details
{
struct dispatcher_RunAsync
{
template<typename Dispatcher, typename... Args>
static void Schedule(Dispatcher const& dispatcher, Args&&... args)
{
dispatcher.RunAsync(std::forward<Args>(args)...);
}
};

struct dispatcher_TryEnqueue
{
template<typename Dispatcher, typename... Args>
static void Schedule(Dispatcher const& dispatcher, Args&&... args)
{
dispatcher.TryEnqueue(std::forward<Args>(args)...);
}
};

template<typename Dispatcher> struct dispatcher_traits;
}

#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
#include <experimental/coroutine>
namespace wil::details
{
template<typename T = void> using coroutine_handle = std::experimental::coroutine_handle<T>;
}
#elif defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L)
#include <coroutine>
namespace wil::details
{
template<typename T = void> using coroutine_handle = std::coroutine_handle<T>;
}
#endif
/// @endcond

#if defined(_RESUMABLE_FUNCTIONS_SUPPORTED) || (defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L))
/// @cond
namespace wil::details
{
struct dispatched_handler_state
{
details::coroutine_handle<> handle{};
bool orphaned = false;
};

struct dispatcher_handler
{
dispatcher_handler(dispatched_handler_state* state) : m_state(state) { }
dispatcher_handler(dispatcher_handler&& other) noexcept : m_state(std::exchange(other.m_state, {})) {}

~dispatcher_handler()
{
if (m_state && m_state->handle)
{
m_state->orphaned = true;
Complete();
}
}
void operator()()
{
Complete();
}

void Complete()
{
auto state = std::exchange(m_state, nullptr);
std::exchange(state->handle, {}).resume();
}

dispatched_handler_state* m_state;
};
}
/// @endcond

namespace wil
{
//! Resumes coroutine execution on the thread associated with the dispatcher, or throws
//! an exception (from an arbitrary thread) if unable. Supported dispatchers are
//! Windows.System.DispatcherQueue, Microsoft.System.DispatcherQueue,
//! Microsoft.UI.Dispatching.DispatcherQueue, and Windows.UI.Core.CoreDispatcher,
//! but you must include the corresponding <winrt/Namespace.h> header before including
//! wil\cppwinrt_helpers.h. It is okay to include wil\cppwinrt_helpers.h multiple times:
//! support will be enabled for any winrt/Namespace.h headers that were included since
//! the previous inclusion of wil\cppwinrt_headers.h.
template<typename Dispatcher>
[[nodiscard]] auto resume_foreground(Dispatcher const& dispatcher,
typename details::dispatcher_traits<Dispatcher>::Priority priority = details::dispatcher_traits<Dispatcher>::Priority::Normal)
{
using Traits = details::dispatcher_traits<Dispatcher>;
using Priority = typename Traits::Priority;
using Handler = typename Traits::Handler;

struct awaitable
{
awaitable(Dispatcher const& dispatcher, Priority priority) noexcept :
m_dispatcher(dispatcher),
m_priority(priority)
{
}
bool await_ready() const noexcept { return false; }

void await_suspend(details::coroutine_handle<> handle)
{
m_state.handle = handle;
Handler handler{ details::dispatcher_handler(&m_state) };
try
{
// The return value of Schedule is not reliable. Use the dispatcher_handler destructor
// to detect whether the work item failed to run.
Traits::Scheduler::Schedule(m_dispatcher, m_priority, handler);
}
catch (...)
{
m_state.handle = nullptr; // the exception will resume the coroutine, so the handler shouldn't do it
throw;
}
}

void await_resume() const
{
if (m_state.orphaned)
{
throw winrt::hresult_error(static_cast<winrt::hresult>(0x800701ab)); // HRESULT_FROM_WIN32(ERROR_NO_TASK_QUEUE)
}
}

private:
Dispatcher const& m_dispatcher;
Priority const m_priority;
details::dispatched_handler_state m_state;
};
return awaitable{ dispatcher, priority };
}
}
#endif // Coroutines are supported

#endif // __WIL_CPPWINRT_HELPERS_DEFINED

/// @cond
#if defined(WINRT_Windows_UI_Core_H) && !defined(__WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS)
#define __WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS
namespace wil::details
{
template<>
struct dispatcher_traits<winrt::Windows::UI::Core::CoreDispatcher>
{
using Priority = winrt::Windows::UI::Core::CoreDispatcherPriority;
using Handler = winrt::Windows::UI::Core::DispatchedHandler;
using Scheduler = dispatcher_RunAsync;
};
}
#endif // __WIL_CPPWINRT_WINDOWS_UI_CORE_HELPERS

#if defined(WINRT_Windows_System_H) && !defined(__WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS)
#define __WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS
namespace wil::details
{
template<>
struct dispatcher_traits<winrt::Windows::System::DispatcherQueue>
{
using Priority = winrt::Windows::System::DispatcherQueuePriority;
using Handler = winrt::Windows::System::DispatcherQueueHandler;
using Scheduler = dispatcher_TryEnqueue;
};
}
#endif // __WIL_CPPWINRT_WINDOWS_SYSTEM_HELPERS

#if defined(WINRT_Microsoft_System_H) && !defined(__WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS)
#define __WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS
namespace wil::details
{
template<>
struct dispatcher_traits<winrt::Microsoft::System::DispatcherQueue>
{
using Priority = winrt::Microsoft::System::DispatcherQueuePriority;
using Handler = winrt::Microsoft::System::DispatcherQueueHandler;
using Scheduler = dispatcher_TryEnqueue;
};
}
#endif // __WIL_CPPWINRT_MICROSOFT_SYSTEM_HELPERS

#if defined(WINRT_Microsoft_UI_Dispatching_H) && !defined(__WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS)
#define __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS
namespace wil::details
{
template<>
struct dispatcher_traits<winrt::Microsoft::UI::Dispatching::DispatcherQueue>
{
using Priority = winrt::Microsoft::UI::Dispatching::DispatcherQueuePriority;
using Handler = winrt::Microsoft::UI::Dispatching::DispatcherQueueHandler;
using Scheduler = dispatcher_TryEnqueue;
};
}
#endif // __WIL_CPPWINRT_MICROSOFT_UI_DISPATCHING_HELPERS
/// @endcond

#if defined(WINRT_Windows_Foundation_Collections_H) && !defined(__WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS)
#define __WIL_CPPWINRT_WINDOWS_FOUNDATION_COLLECTION_HELPERS
namespace wil
{
/// @cond
namespace details
{
template<typename T> struct is_winrt_vector_like {
private:
template <typename U,
typename = decltype(std::declval<U>().GetMany(std::declval<U>().Size(),
winrt::array_view<decltype(std::declval<U>().GetAt(0))>{}))>
static constexpr bool get_value(int) { return true; }
template <typename> static constexpr bool get_value(...) { return false; }
public:
static constexpr bool value = get_value<T>(0);
};

template<typename T> struct is_winrt_iterator_like {
private:
template <typename U,
typename = decltype(std::declval<U>().GetMany(winrt::array_view<decltype(std::declval<U>().Current())>{}))>
static constexpr bool get_value(int) { return true; }
template <typename> static constexpr bool get_value(...) { return false; }
public:
static constexpr bool value = get_value<T>(0);
};

template<typename T> constexpr T empty() noexcept
{
if constexpr (std::is_base_of_v<winrt::Windows::Foundation::IUnknown, T>)
{
return nullptr;
}
else
{
return {};
}
}
}
/// @endcond

/** Converts C++ / WinRT vectors, iterators, and iterables to std::vector by requesting the
collection's data in bulk. This can be more efficient in terms of IPC cost than iteratively
processing the collection.
~~~
winrt::IVector<winrt::hstring> collection = GetCollection();
std::vector<winrt::hstring> allData = wil::to_vector(collection); // read all data from collection
for (winrt::hstring const& item : allData)
{
// use item
}
~~~
Can be used for IVector<T>, IVectorView<T>, IIterable<T>, IIterator<T>, and any type or
interface that C++/WinRT projects those interfaces for (PropertySet, IMap<T,K>, etc.)
Iterable-only types fetch content in units of 64. When used with an iterator, the returned
vector contains the iterator's current position and any others after it.
*/
template<typename TSrc> auto to_vector(TSrc const& src)
{
if constexpr (details::is_winrt_vector_like<TSrc>::value)
{
using T = decltype(src.GetAt(0));
std::vector<T> result;
if (auto expected = src.Size())
{
result.resize(expected + 1, details::empty<T>());
auto actual = src.GetMany(0, result);
if (actual > expected)
{
throw winrt::hresult_changed_state();
}
result.resize(actual);
}
return result;
}
else if constexpr (details::is_winrt_iterator_like<TSrc>::value)
{
using T = decltype(src.Current());
std::vector<T> result;
constexpr uint32_t chunkSize = 64;
while (true)
{
auto const lastSize = result.size();
result.resize(lastSize + chunkSize, details::empty<T>());
auto fetched = src.GetMany({result.data() + lastSize, result.data() + lastSize + chunkSize });
if (fetched < chunkSize)
{
result.resize(lastSize + fetched);
break;
}
}
return result;
}
else
{
return to_vector(src.First());
}
}
}
#endif

#if defined(WINRT_Windows_UI_H) && defined(_WINDOWS_UI_INTEROP_H_) && !defined(__WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS)
#define __WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS
#if !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) && !defined(MIDL_NS_PREFIX)
#pragma push_macro("ABI")
#undef ABI
#define ABI
#endif

namespace wil
{
#if defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_CU)
//! The following methods require that you include both <winrt/Windows.UI.h>
//! <Windows.UI.Interop.h> before including wil/cppwinrt_helpers.h, and that NTDDI_VERSION
//! is at least NTDDI_WIN10_CU. It is okay to include wil\cppwinrt_helpers.h multiple times:
//! support will be enabled for any headers that were included since the previous inclusion
//! of wil\cppwinrt_headers.h.
inline winrt::Windows::UI::WindowId GetWindowIdFromWindow(HWND hwnd)
{
ABI::Windows::UI::WindowId abiWindowId;
winrt::check_hresult(::GetWindowIdFromWindow(hwnd, &abiWindowId));
return winrt::Windows::UI::WindowId{ abiWindowId.Value };
}

inline HWND GetWindowFromWindowId(winrt::Windows::UI::WindowId windowId)
{
HWND hwnd;
winrt::check_hresult(::GetWindowFromWindowId({ windowId.Value }, &hwnd));
return hwnd;
}
#endif /*defined(NTDDI_VERSION) && (NTDDI_VERSION >= NTDDI_WIN10_CU)*/
}

#if !defined(____x_ABI_CWindows_CFoundation_CIClosable_FWD_DEFINED__) && !defined(MIDL_NS_PREFIX)
#pragma pop_macro("ABI")
#endif
#endif // __WIL_CPPWINRT_WINDOWS_UI_INTEROP_HELPERS
@@ -0,0 +1,74 @@
//*********************************************************
//
// 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_WRL_INCLUDED
#define __WIL_CPPWINRT_WRL_INCLUDED

#include "cppwinrt.h"
#include <winrt\base.h>

#include "result_macros.h"
#include <wrl\module.h>

// wil::wrl_factory_for_winrt_com_class provides interopability between a
// C++/WinRT class and the WRL Module system, allowing the winrt class to be
// CoCreatable.
//
// Usage:
// - In your cpp, add:
// CoCreatableCppWinRtClass(className)
//
// - In the dll.cpp (or equivalent) for the module containing your class, add:
// CoCreatableClassWrlCreatorMapInclude(className)
//
namespace wil
{
namespace details
{
template <typename TCppWinRTClass>
class module_count_wrapper : public TCppWinRTClass
{
public:
module_count_wrapper()
{
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
{
modulePtr->IncrementObjectCount();
}
}

virtual ~module_count_wrapper()
{
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
{
modulePtr->DecrementObjectCount();
}
}
};
}

template <typename TCppWinRTClass>
class wrl_factory_for_winrt_com_class : public ::Microsoft::WRL::ClassFactory<>
{
public:
IFACEMETHODIMP CreateInstance(_In_opt_ ::IUnknown* unknownOuter, REFIID riid, _COM_Outptr_ void **object) noexcept try
{
*object = nullptr;
RETURN_HR_IF(CLASS_E_NOAGGREGATION, unknownOuter != nullptr);

return winrt::make<details::module_count_wrapper<TCppWinRTClass>>().as(riid, object);
}
CATCH_RETURN()
};
}

#define CoCreatableCppWinRtClass(className) CoCreatableClassWithFactory(className, ::wil::wrl_factory_for_winrt_com_class<className>)

#endif // __WIL_CPPWINRT_WRL_INCLUDED
@@ -31,6 +31,7 @@ namespace wil
return wcsncmp(path, L"\\\\?\\", 4) == 0;
}

#if (_WIN32_WINNT >= _WIN32_WINNT_WIN7)
//! 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)
@@ -51,6 +52,7 @@ namespace wil
}
return result;
}
#endif

//! Determine if the file name is one of the special "." or ".." names.
inline bool path_is_dot_or_dotdot(_In_ PCWSTR fileName)
@@ -83,7 +85,7 @@ namespace wil
return false;
}

#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7)

// PathCch.h APIs are only in desktop API for now.

@@ -111,7 +113,7 @@ namespace wil
{
if (::CreateDirectoryW(path, nullptr) == FALSE)
{
DWORD const lastError = ::GetLastError();
DWORD lastError = ::GetLastError();
if (lastError == ERROR_PATH_NOT_FOUND)
{
size_t parentLength;
@@ -120,9 +122,16 @@ namespace wil
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_FAILED(CreateDirectoryDeepNoThrow(parent.get())); // recurs
}
if (::CreateDirectoryW(path, nullptr) == FALSE)
{
lastError = ::GetLastError();
if (lastError != ERROR_ALREADY_EXISTS)
{
RETURN_WIN32(lastError);
}
}
RETURN_IF_WIN32_BOOL_FALSE(::CreateDirectoryW(path, nullptr));
}
else if (lastError != ERROR_ALREADY_EXISTS)
{
@@ -183,13 +192,53 @@ namespace wil
enum class RemoveDirectoryOptions
{
None = 0,
KeepRootDirectory = 0x1
KeepRootDirectory = 0x1,
RemoveReadOnly = 0x2,
};
DEFINE_ENUM_FLAG_OPERATORS(RemoveDirectoryOptions);

namespace details
{
// Reparse points should not be traversed in most recursive walks of the file system,
// unless allowed through the appropriate reparse tag.
inline bool CanRecurseIntoDirectory(const FILE_ATTRIBUTE_TAG_INFO& info)
{
return (WI_IsFlagSet(info.FileAttributes, FILE_ATTRIBUTE_DIRECTORY) &&
(WI_IsFlagClear(info.FileAttributes, FILE_ATTRIBUTE_REPARSE_POINT) ||
(IsReparseTagDirectory(info.ReparseTag) || (info.ReparseTag == IO_REPARSE_TAG_WCI))));
}
}

// Retrieve a handle to a directory only if it is safe to recurse into.
inline wil::unique_hfile TryCreateFileCanRecurseIntoDirectory(PCWSTR path, PWIN32_FIND_DATAW fileFindData, DWORD access = GENERIC_READ | /*DELETE*/ 0x00010000L, DWORD share = FILE_SHARE_READ)
{
wil::unique_hfile result(CreateFileW(path, access, share,
nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr));
if (result)
{
FILE_ATTRIBUTE_TAG_INFO fati;
if (GetFileInformationByHandleEx(result.get(), FileAttributeTagInfo, &fati, sizeof(fati)) &&
details::CanRecurseIntoDirectory(fati))
{
if (fileFindData)
{
// Refresh the found file's data now that we have secured the directory from external manipulation.
fileFindData->dwFileAttributes = fati.FileAttributes;
fileFindData->dwReserved0 = fati.ReparseTag;
}
}
else
{
result.reset();
}
}

return result;
}

// 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
inline HRESULT RemoveDirectoryRecursiveNoThrow(PCWSTR inputPath, RemoveDirectoryOptions options = RemoveDirectoryOptions::None, HANDLE deleteHandle = INVALID_HANDLE_VALUE) WI_NOEXCEPT
{
wil::unique_hlocal_string path;
PATHCCH_OPTIONS combineOptions = PATHCCH_NONE;
@@ -228,14 +277,50 @@ namespace wil
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)));
// Get a handle to the directory to delete, preventing it from being replaced to prevent writes which could be used
// to bypass permission checks, and verify that it is not a name surrogate (e.g. symlink, mount point, etc).
wil::unique_hfile recursivelyDeletableDirectoryHandle = TryCreateFileCanRecurseIntoDirectory(pathToDelete.get(), &fd);
if (recursivelyDeletableDirectoryHandle)
{
RemoveDirectoryOptions localOptions = options;
RETURN_IF_FAILED(RemoveDirectoryRecursiveNoThrow(pathToDelete.get(), WI_ClearFlag(localOptions, RemoveDirectoryOptions::KeepRootDirectory), recursivelyDeletableDirectoryHandle.get()));
}
else if (WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_REPARSE_POINT))
{
// This is a directory reparse point that should not be recursed. Delete it without traversing into it.
RETURN_IF_WIN32_BOOL_FALSE(::RemoveDirectoryW(pathToDelete.get()));
}
else
{
// Failed to grab a handle to the file or to read its attributes. This is not safe to recurse.
RETURN_WIN32(::GetLastError());
}
}
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()));
// Try a DeleteFile. Some errors may be recoverable.
if (!::DeleteFileW(pathToDelete.get()))
{
// Fail for anything other than ERROR_ACCESS_DENIED with option to RemoveReadOnly available
bool potentiallyFixableReadOnlyProblem =
WI_IsFlagSet(options, RemoveDirectoryOptions::RemoveReadOnly) && ::GetLastError() == ERROR_ACCESS_DENIED;
RETURN_LAST_ERROR_IF(!potentiallyFixableReadOnlyProblem);

// Fail if the file does not have read-only set, likely just an ACL problem
DWORD fileAttr = ::GetFileAttributesW(pathToDelete.get());
RETURN_LAST_ERROR_IF(!WI_IsFlagSet(fileAttr, FILE_ATTRIBUTE_READONLY));

// Remove read-only flag, setting to NORMAL if completely empty
WI_ClearFlag(fileAttr, FILE_ATTRIBUTE_READONLY);
if (fileAttr == 0)
{
fileAttr = FILE_ATTRIBUTE_NORMAL;
}

// Set the new attributes and try to delete the file again, returning any failure
::SetFileAttributesW(pathToDelete.get(), fileAttr);
RETURN_IF_WIN32_BOOL_FALSE(::DeleteFileW(pathToDelete.get()));
}
}
}

@@ -252,7 +337,35 @@ namespace wil

if (WI_IsFlagClear(options, RemoveDirectoryOptions::KeepRootDirectory))
{
RETURN_IF_WIN32_BOOL_FALSE(::RemoveDirectoryW(path.get()));
if (deleteHandle != INVALID_HANDLE_VALUE)
{
#if (NTDDI_VERSION >= NTDDI_WIN10_RS1)
// DeleteFile and RemoveDirectory use POSIX delete, falling back to non-POSIX on most errors. Do the same here.
FILE_DISPOSITION_INFO_EX fileInfoEx{};
fileInfoEx.Flags = FILE_DISPOSITION_FLAG_DELETE | FILE_DISPOSITION_FLAG_POSIX_SEMANTICS;
if (!SetFileInformationByHandle(deleteHandle, FileDispositionInfoEx, &fileInfoEx, sizeof(fileInfoEx)))
{
auto const err = ::GetLastError();
// The real error we're looking for is STATUS_CANNOT_DELETE, but that's mapped to ERROR_ACCESS_DENIED.
if (err != ERROR_ACCESS_DENIED)
{
#endif
FILE_DISPOSITION_INFO fileInfo{};
fileInfo.DeleteFile = TRUE;
RETURN_IF_WIN32_BOOL_FALSE(SetFileInformationByHandle(deleteHandle, FileDispositionInfo, &fileInfo, sizeof(fileInfo)));
#if (NTDDI_VERSION >= NTDDI_WIN10_RS1)
}
else
{
RETURN_WIN32(err);
}
}
#endif
}
else
{
RETURN_IF_WIN32_BOOL_FALSE(::RemoveDirectoryW(path.get()));
}
}
return S_OK;
}
@@ -552,7 +665,7 @@ namespace wil
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.
unsigned 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; }
@@ -596,7 +709,6 @@ namespace wil
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)))
@@ -613,19 +725,17 @@ namespace wil
}
else
{
requeue = false;
// No need to requeue
return;
}

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)
{
// 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
}
readerState->StartIo(); // ignoring failure here
}
}

@@ -798,7 +908,7 @@ namespace wil

// 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)
_Outptr_result_maybenull_ void **result)
{
*result = nullptr;

@@ -875,6 +985,36 @@ namespace wil
return S_OK;
}

// Verifies that the given file path is not a hard or a soft link. If the file is present at the path, returns
// a handle to it without delete permissions to block an attacker from swapping the file.
inline HRESULT CreateFileAndEnsureNotLinked(PCWSTR path, wil::unique_hfile& fileHandle)
{
// Open handles to the original path and to the final path and compare each file's information
// to verify they are the same file. If they are different, the file is a soft link.
fileHandle.reset(CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, nullptr));
RETURN_LAST_ERROR_IF(!fileHandle);
BY_HANDLE_FILE_INFORMATION fileInfo;
RETURN_IF_WIN32_BOOL_FALSE(GetFileInformationByHandle(fileHandle.get(), &fileInfo));

// Open a handle without the reparse point flag to get the final path in case it is a soft link.
wil::unique_hfile finalPathHandle(CreateFileW(path, 0, 0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr));
RETURN_LAST_ERROR_IF(!finalPathHandle);
BY_HANDLE_FILE_INFORMATION finalFileInfo;
RETURN_IF_WIN32_BOOL_FALSE(GetFileInformationByHandle(finalPathHandle.get(), &finalFileInfo));
finalPathHandle.reset();

// The low and high indices and volume serial number uniquely identify a file. These must match if they are the same file.
const bool isSoftLink =
((fileInfo.nFileIndexLow != finalFileInfo.nFileIndexLow) ||
(fileInfo.nFileIndexHigh != finalFileInfo.nFileIndexHigh) ||
(fileInfo.dwVolumeSerialNumber != finalFileInfo.dwVolumeSerialNumber));

// Return failure if it is a soft link or a hard link (number of links greater than 1).
RETURN_HR_IF(HRESULT_FROM_WIN32(ERROR_BAD_PATHNAME), (isSoftLink || fileInfo.nNumberOfLinks > 1));

return S_OK;
}

#ifdef _CPPUNWIND
/** Get file information for a fixed sized structure, throws on failure.
~~~
@@ -902,7 +1042,7 @@ namespace wil
return result;
}
#endif // _CPPUNWIND
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7)
}

#endif // __WIL_FILESYSTEM_INCLUDED
@@ -0,0 +1,168 @@
//*********************************************************
//
// 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_NT_RESULTMACROS_INCLUDED
#define __WIL_NT_RESULTMACROS_INCLUDED

#include "result_macros.h"

// Helpers for return macros
#define __NT_RETURN_NTSTATUS(status, str) __WI_SUPPRESS_4127_S do { NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { __R_FN(Return_NtStatus)(__R_INFO(str) __status); } return __status; } __WI_SUPPRESS_4127_E while ((void)0, 0)
#define __NT_RETURN_NTSTATUS_MSG(status, str, fmt, ...) __WI_SUPPRESS_4127_S do { NTSTATUS __status = (status); if (FAILED_NTSTATUS(__status)) { __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, ##__VA_ARGS__); } return __status; } __WI_SUPPRESS_4127_E while ((void)0, 0)

//*****************************************************************************
// Macros for returning failures as NTSTATUS
//*****************************************************************************

// Always returns a known result (NTSTATUS) - always logs failures
#define NT_RETURN_NTSTATUS(status) __NT_RETURN_NTSTATUS(wil::verify_ntstatus(status), #status)

// Always returns a known failure (NTSTATUS) - always logs a var-arg message on failure
#define NT_RETURN_NTSTATUS_MSG(status, fmt, ...) __NT_RETURN_NTSTATUS_MSG(wil::verify_ntstatus(status), #status, fmt, ##__VA_ARGS__)

// Conditionally returns failures (NTSTATUS) - always logs failures
#define NT_RETURN_IF_NTSTATUS_FAILED(status) __WI_SUPPRESS_4127_S do { const auto __statusRet = wil::verify_ntstatus(status); if (FAILED_NTSTATUS(__statusRet)) { __NT_RETURN_NTSTATUS(__statusRet, #status); }} __WI_SUPPRESS_4127_E while ((void)0, 0)

// Conditionally returns failures (NTSTATUS) - always logs a var-arg message on failure
#define NT_RETURN_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) __WI_SUPPRESS_4127_S do { const auto __statusRet = wil::verify_ntstatus(status); if (FAILED_NTSTATUS(__statusRet)) { __NT_RETURN_NTSTATUS_MSG(__statusRet, #status, fmt, ##__VA_ARGS__); }} __WI_SUPPRESS_4127_E while((void)0, 0)

//*****************************************************************************
// Macros to catch and convert exceptions on failure
//*****************************************************************************

// Use these macros *within* a catch (...) block to handle exceptions
#define NT_RETURN_CAUGHT_EXCEPTION() return __R_FN(Nt_Return_CaughtException)(__R_INFO_ONLY(nullptr))
#define NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ...) return __R_FN(Nt_Return_CaughtExceptionMsg)(__R_INFO(nullptr) fmt, ##__VA_ARGS__)

// Use these macros in place of a catch block to handle exceptions
#define NT_CATCH_RETURN() catch (...) { NT_RETURN_CAUGHT_EXCEPTION(); }
#define NT_CATCH_RETURN_MSG(fmt, ...) catch (...) { NT_RETURN_CAUGHT_EXCEPTION_MSG(fmt, ##__VA_ARGS__); }


namespace wil
{
//*****************************************************************************
// Public Helpers that catch -- mostly only enabled when exceptions are enabled
//*****************************************************************************

// StatusFromCaughtException is a function that is meant to be called from within a catch(...) block. Internally
// it re-throws and catches the exception to convert it to an NTSTATUS. If an exception is of an unrecognized type
// the function will fail fast.
//
// try
// {
// // Code
// }
// catch (...)
// {
// status = wil::StatusFromCaughtException();
// }
_Always_(_Post_satisfies_(return < 0))
__declspec(noinline) inline NTSTATUS StatusFromCaughtException() WI_NOEXCEPT
{
bool isNormalized = false;
NTSTATUS status = STATUS_SUCCESS;
if (details::g_pfnResultFromCaughtExceptionInternal)
{
status = details::g_pfnResultFromCaughtExceptionInternal(nullptr, 0, &isNormalized).status;
}
if (FAILED_NTSTATUS(status))
{
return status;
}

// Caller bug: an unknown exception was thrown
__WIL_PRIVATE_FAIL_FAST_HR_IF(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION), g_fResultFailFastUnknownExceptions);
return wil::details::HrToNtStatus(__HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
}

namespace details
{
template<FailureType>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported = SupportedExceptions::Default);
template<FailureType>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList);

namespace __R_NS_NAME
{
#ifdef WIL_ENABLE_EXCEPTIONS
__R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT
{
__R_FN_LOCALS;
return wil::details::ReportStatus_CaughtException<FailureType::Return>(__R_DIRECT_FN_CALL_ONLY);
}

__R_DIRECT_METHOD(NTSTATUS, Nt_Return_CaughtExceptionMsg)(__R_DIRECT_FN_PARAMS _Printf_format_string_ PCSTR formatString, ...) WI_NOEXCEPT
{
va_list argList;
va_start(argList, formatString);
__R_FN_LOCALS;
return wil::details::ReportStatus_CaughtExceptionMsg<FailureType::Return>(__R_DIRECT_FN_CALL formatString, argList);
}
#endif
}

template<FailureType T>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException(__R_FN_PARAMS_FULL, SupportedExceptions supported)
{
wchar_t message[2048];
message[0] = L'\0';
return ReportFailure_CaughtExceptionCommon<T>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status;
}

template<>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException<FailureType::FailFast>(__R_FN_PARAMS_FULL, SupportedExceptions supported)
{
wchar_t message[2048];
message[0] = L'\0';
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::FailFast>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status);
}

template<>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtException<FailureType::Exception>(__R_FN_PARAMS_FULL, SupportedExceptions supported)
{
wchar_t message[2048];
message[0] = L'\0';
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::Exception>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), supported).status);
}

template<FailureType T>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList)
{
// Pre-populate the buffer with our message, the exception message will be added to it...
wchar_t message[2048];
PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList);
StringCchCatW(message, ARRAYSIZE(message), L" -- ");
return ReportFailure_CaughtExceptionCommon<T>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status;
}

template<>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg<FailureType::FailFast>(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList)
{
// Pre-populate the buffer with our message, the exception message will be added to it...
wchar_t message[2048];
PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList);
StringCchCatW(message, ARRAYSIZE(message), L" -- ");
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::FailFast>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status);
}

template<>
__declspec(noinline) inline NTSTATUS ReportStatus_CaughtExceptionMsg<FailureType::Exception>(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList)
{
// Pre-populate the buffer with our message, the exception message will be added to it...
wchar_t message[2048];
PrintLoggingMessage(message, ARRAYSIZE(message), formatString, argList);
StringCchCatW(message, ARRAYSIZE(message), L" -- ");
RESULT_NORETURN_RESULT(ReportFailure_CaughtExceptionCommon<FailureType::Exception>(__R_FN_CALL_FULL, message, ARRAYSIZE(message), SupportedExceptions::Default).status);
}
}
}

#endif // __WIL_NT_RESULTMACROS_INCLUDED

Large diffs are not rendered by default.

@@ -252,15 +252,17 @@ namespace wil
if (ProcessShutdownInProgress())
{
// There are no other threads to contend with.
if (--m_refCount == 0)
m_refCount = m_refCount - 1;
if (m_refCount == 0)
{
m_data.ProcessShutdown();
}
}
else
{
auto lock = m_mutex.acquire();
if (--m_refCount == 0)
m_refCount = m_refCount - 1;
if (m_refCount == 0)
{
// We must explicitly destroy our semaphores while holding the mutex
m_value.Destroy();
@@ -281,7 +283,7 @@ namespace wil

const DWORD size = static_cast<DWORD>(sizeof(ProcessLocalStorageData<T>));
wchar_t name[MAX_PATH];
WI_VERIFY(SUCCEEDED(StringCchPrintfW(name, ARRAYSIZE(name), L"Local\\SM0:%d:%d:%hs", ::GetCurrentProcessId(), size, staticNameWithVersion)));
WI_VERIFY(SUCCEEDED(StringCchPrintfW(name, ARRAYSIZE(name), L"Local\\SM0:%lu:%lu:%hs", ::GetCurrentProcessId(), size, staticNameWithVersion)));

unique_mutex_nothrow mutex;
mutex.reset(::CreateMutexExW(nullptr, name, 0, MUTEX_ALL_ACCESS));
@@ -295,7 +297,7 @@ namespace wil
if (pointer)
{
*data = reinterpret_cast<ProcessLocalStorageData<T>*>(pointer);
(*data)->m_refCount++;
(*data)->m_refCount = (*data)->m_refCount + 1;
}
else
{
@@ -312,13 +314,13 @@ namespace wil
SemaphoreValue m_value;
T m_data;

static HRESULT MakeAndInitialize(PCWSTR name, unique_mutex_nothrow&& mutex, ProcessLocalStorageData<T>** data)
static HRESULT MakeAndInitialize(PCWSTR name, unique_mutex_nothrow&& mutex, _Outptr_result_nullonfailure_ ProcessLocalStorageData<T>** data)
{
*data = nullptr;

const DWORD size = static_cast<DWORD>(sizeof(ProcessLocalStorageData<T>));

unique_process_heap_ptr<ProcessLocalStorageData<T>> dataAlloc(static_cast<ProcessLocalStorageData<T>*>(::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, size)));
unique_process_heap_ptr<ProcessLocalStorageData<T>> dataAlloc(static_cast<ProcessLocalStorageData<T>*>(details::ProcessHeapAlloc(HEAP_ZERO_MEMORY, size)));
__WIL_PRIVATE_RETURN_IF_NULL_ALLOC(dataAlloc);

SemaphoreValue semaphoreValue;
@@ -406,10 +408,9 @@ namespace wil

if (shouldAllocate)
{
Node *pNew = reinterpret_cast<Node *>(::HeapAlloc(::GetProcessHeap(), 0, sizeof(Node)));
if (pNew != nullptr)
if (auto pNewRaw = details::ProcessHeapAlloc(0, sizeof(Node)))
{
new(pNew)Node{ threadId };
auto pNew = new (pNewRaw) Node{ threadId };

Node *pFirst;
do
@@ -428,7 +429,7 @@ namespace wil

struct Node
{
DWORD threadId;
DWORD threadId = ULONG_MAX;
Node* pNext = nullptr;
T value{};
};
@@ -487,7 +488,7 @@ namespace wil

if (!stringBuffer || (stringBufferSize < neededSize))
{
auto newBuffer = ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, neededSize);
auto newBuffer = details::ProcessHeapAlloc(HEAP_ZERO_MEMORY, neededSize);
if (newBuffer)
{
::HeapFree(::GetProcessHeap(), 0, stringBuffer);
@@ -508,7 +509,7 @@ namespace wil
}
}

void Get(FailureInfo& info)
void Get(FailureInfo& info) const
{
::ZeroMemory(&info, sizeof(info));

@@ -565,7 +566,7 @@ namespace wil
if (!errors && create)
{
const unsigned short errorCount = 5;
errors = reinterpret_cast<ThreadLocalFailureInfo *>(::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, errorCount * sizeof(ThreadLocalFailureInfo)));
errors = reinterpret_cast<ThreadLocalFailureInfo *>(details::ProcessHeapAlloc(HEAP_ZERO_MEMORY, errorCount * sizeof(ThreadLocalFailureInfo)));
if (errors)
{
errorAllocCount = errorCount;
@@ -611,7 +612,7 @@ namespace wil
errors[errorCurrentIndex].Set(info, ::InterlockedIncrementNoFence(failureSequenceId));
}

bool GetLastError(_Inout_ wil::FailureInfo& info, unsigned int minSequenceId, HRESULT matchRequirement)
bool GetLastError(_Inout_ wil::FailureInfo& info, unsigned int minSequenceId, HRESULT matchRequirement) const
{
if (!errors)
{
@@ -678,7 +679,7 @@ namespace wil
// NOTE: FailureType::Log as it's only informative (no action) and SupportedExceptions::All as it's not a barrier, only recognition.
wchar_t message[2048];
message[0] = L'\0';
const HRESULT hr = details::ReportFailure_CaughtExceptionCommon<FailureType::Log>(__R_DIAGNOSTICS_RA(source, returnAddress), message, ARRAYSIZE(message), SupportedExceptions::All);
const HRESULT hr = details::ReportFailure_CaughtExceptionCommon<FailureType::Log>(__R_DIAGNOSTICS_RA(source, returnAddress), message, ARRAYSIZE(message), SupportedExceptions::All).hr;

// Now that the exception was logged, we should be able to fetch it.
return GetLastError(info, minSequenceId, hr);
@@ -958,7 +959,7 @@ namespace wil
m_ppThreadList = nullptr;
}

bool IsWatching()
bool IsWatching() const
{
return (m_threadId != 0);
}
@@ -1044,7 +1045,9 @@ namespace wil

if (g_pfnTelemetryCallback != nullptr)
{
g_pfnTelemetryCallback(reportedTelemetry, *pFailure);
// If the telemetry was requested to be suppressed,
// pretend like it has already been reported to the fallback callback
g_pfnTelemetryCallback(reportedTelemetry || WI_IsFlagSet(pFailure->flags, FailureFlags::RequestSuppressTelemetry), *pFailure);
}
}

@@ -1079,7 +1082,7 @@ namespace wil
{
}

bool NotifyFailure(FailureInfo const &failure) WI_NOEXCEPT
bool NotifyFailure(FailureInfo const &failure) WI_NOEXCEPT override
{
return m_errorFunction(failure);
}
@@ -1251,7 +1254,7 @@ namespace wil
return (FAILED(m_failure.GetFailureInfo().hr) ? &(m_failure.GetFailureInfo()) : nullptr);
}

bool NotifyFailure(FailureInfo const &failure) WI_NOEXCEPT
bool NotifyFailure(FailureInfo const &failure) WI_NOEXCEPT override
{
// When we "cache" a failure, we bias towards trying to find the origin of the last HRESULT
// generated, so we ignore subsequent failures on the same error code (assuming propagation).

Large diffs are not rendered by default.

@@ -31,7 +31,6 @@
#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
@@ -82,15 +81,45 @@ namespace wil
}
}
}

// This method will check for the presence of stowed exception data on the current thread. If such data exists, and the HRESULT
// matches the current failure, then we will call RoFailFastWithErrorContext. RoFailFastWithErrorContext in this situation will
// result in -VASTLY- improved crash bucketing. It is hard to express just how much better. In other cases we just return and
// the calling method fails fast the same way it always has.
inline void __stdcall FailfastWithContextCallback(wil::FailureInfo const& failure) WI_NOEXCEPT
{
wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK)
{
wil::unique_bstr descriptionUnused;
HRESULT existingHr = failure.hr;
wil::unique_bstr restrictedDescriptionUnused;
wil::unique_bstr capabilitySidUnused;
if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)) &&
(existingHr == failure.hr))
{
// GetRestrictedErrorInfo returns ownership of the error information. We want it to be available for RoFailFastWithErrorContext
// so we must restore it via SetRestrictedErrorInfo first.
SetRestrictedErrorInfo(restrictedErrorInformation.get());
RoFailFastWithErrorContext(existingHr);
}
else
{
// The error didn't match the current failure. Put it back in thread-local storage even though we aren't failing fast
// in this method, so it is available in the debugger just-in-case.
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);
::wil::SetFailfastWithContextCallback(::wil::details::FailfastWithContextCallback);
return 1;
});
#endif // __cplusplus_winrt
})

#endif // __WIL_RESULT_ORIGINATE_INCLUDED
@@ -121,76 +121,76 @@ namespace wil
// 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;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, char> = LongLongToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, int> = LongLongToInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, long> = LongLongToLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, short> = LongLongToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, signed char> = LongLongToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned __int64> = LongLongToULongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned char> = LongLongToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned int> = LongLongToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned long> = LongLongToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned short> = LongLongToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, char> = IntToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, short> = IntToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, signed char> = IntToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned __int64> = IntToULongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned char> = IntToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned int> = IntToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned long> = IntToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned short> = IntToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, char> = LongToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, int> = LongToInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, short> = LongToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, signed char> = LongToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned __int64> = LongToULongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned char> = LongToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned int> = LongToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned long> = LongToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned short> = LongToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, char> = ShortToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, signed char> = ShortToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned __int64> = ShortToULongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned char> = ShortToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned int> = ShortToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned long> = ShortToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned short> = ShortToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned __int64> = Int8ToULongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned char> = Int8ToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned int> = Int8ToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned long> = Int8ToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned short> = Int8ToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, __int64> = ULongLongToLongLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, char> = ULongLongToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, int> = ULongLongToInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, long> = ULongLongToLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, short> = ULongLongToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, signed char> = ULongLongToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned char> = ULongLongToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned int> = ULongLongToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned long> = ULongLongToULong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned short> = ULongLongToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned char, char> = UInt8ToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned char, signed char> = UIntToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, char> = UIntToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, int> = UIntToInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, long> = UIntToLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, short> = UIntToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, signed char> = UIntToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, unsigned char> = UIntToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, unsigned short> = UIntToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, char> = ULongToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, int> = ULongToInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, long> = ULongToLong;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, short> = ULongToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, signed char> = ULongToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, unsigned char> = ULongToUChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, unsigned int> = ULongToUInt;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, unsigned short> = ULongToUShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, char> = UShortToChar;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, short> = UShortToShort;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, signed char> = UShortToInt8;
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, unsigned char> = UShortToUChar;
}

// Unsafe conversion where failure results in fail fast.
@@ -15,19 +15,17 @@
#include "resource.h"
#include <memory>
#include <string>
#include <vector>
#include <utility>
#if _HAS_CXX17
#include <string_view>
#endif

#if defined(WIL_ENABLE_EXCEPTIONS)
namespace std
{
template<class _Ty, class _Alloc>
class vector;
#ifndef WI_STL_FAIL_FAST_IF
#define WI_STL_FAIL_FAST_IF FAIL_FAST_IF
#endif

template<class _Elem>
struct char_traits;

template<class _Elem, class _Traits, class _Alloc>
class basic_string;
} // namespace std
#if defined(WIL_ENABLE_EXCEPTIONS)

namespace wil
{
@@ -42,7 +40,7 @@ namespace wil
template<typename Other>
struct rebind
{
typedef secure_allocator<Other> other;
using other = secure_allocator<Other>;
};

secure_allocator()
@@ -100,6 +98,8 @@ namespace wil

wchar_t* buffer() { return &m_value[0]; }

HRESULT trim_at_existing_null(size_t length) { m_value.erase(length); return S_OK; }

std::wstring release() { return std::wstring(std::move(m_value)); }

static PCWSTR get(const std::wstring& value) { return value.c_str(); }
@@ -117,6 +117,78 @@ namespace wil
return str.c_str();
}

#if _HAS_CXX17
/**
zstring_view. A zstring_view is identical to a std::string_view except it is always nul-terminated (unless empty).
* zstring_view can be used for storing string literals without "forgetting" the length or that it is nul-terminated.
* A zstring_view can be converted implicitly to a std::string_view because it is always safe to use a nul-terminated
string_view as a plain string view.
* A zstring_view can be constructed from a std::string because the data in std::string is nul-terminated.
*/
template<class TChar>
class basic_zstring_view : public std::basic_string_view<TChar>
{
using size_type = typename std::basic_string_view<TChar>::size_type;

public:
constexpr basic_zstring_view() noexcept = default;
constexpr basic_zstring_view(const basic_zstring_view&) noexcept = default;
constexpr basic_zstring_view& operator=(const basic_zstring_view&) noexcept = default;

constexpr basic_zstring_view(const TChar* pStringData, size_type stringLength) noexcept
: std::basic_string_view<TChar>(pStringData, stringLength)
{
if (pStringData[stringLength] != 0) { WI_STL_FAIL_FAST_IF(true); }
}

template<size_t stringArrayLength>
constexpr basic_zstring_view(const TChar(&stringArray)[stringArrayLength]) noexcept
: std::basic_string_view<TChar>(&stringArray[0], length_n(&stringArray[0], stringArrayLength))
{
}

// Construct from nul-terminated char ptr. To prevent this from overshadowing array construction,
// we disable this constructor if the value is an array (including string literal).
template<typename TPtr, std::enable_if_t<
std::is_convertible<TPtr, const TChar*>::value && !std::is_array<TPtr>::value>* = nullptr>
constexpr basic_zstring_view(TPtr&& pStr) noexcept
: std::basic_string_view<TChar>(std::forward<TPtr>(pStr)) {}

constexpr basic_zstring_view(const std::basic_string<TChar>& str) noexcept
: std::basic_string_view<TChar>(&str[0], str.size()) {}

// basic_string_view [] precondition won't let us read view[view.size()]; so we define our own.
constexpr const TChar& operator[](size_type idx) const noexcept
{
WI_ASSERT(idx <= this->size() && this->data() != nullptr);
return this->data()[idx];
}

constexpr const TChar* c_str() const noexcept
{
WI_ASSERT(this->data() == nullptr || this->data()[this->size()] == 0);
return this->data();
}

private:
// Bounds-checked version of char_traits::length, like strnlen. Requires that the input contains a null terminator.
static constexpr size_type length_n(_In_reads_opt_(buf_size) const TChar* str, size_type buf_size) noexcept
{
const std::basic_string_view<TChar> view(str, buf_size);
auto pos = view.find_first_of(TChar());
if (pos == view.npos) { WI_STL_FAIL_FAST_IF(true); }
return pos;
}

// The following basic_string_view methods must not be allowed because they break the nul-termination.
using std::basic_string_view<TChar>::swap;
using std::basic_string_view<TChar>::remove_suffix;
};

using zstring_view = basic_zstring_view<char>;
using zwstring_view = basic_zstring_view<wchar_t>;
#endif // _HAS_CXX17

} // namespace wil

#endif // WIL_ENABLE_EXCEPTIONS