update wil to 0b2d6c2d822bb301e7558a14ee66d567c14f5dc7
This commit is contained in:
parent
95ce41ac56
commit
69c335ca8c
|
@ -1,13 +1,21 @@
|
||||||
cmake_minimum_required(VERSION 3.11)
|
cmake_minimum_required(VERSION 3.15)
|
||||||
project(WIL)
|
project(WIL)
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
# Set by build server to speed up build/reduce file/object size
|
# Set by build server to speed up build/reduce file/object size
|
||||||
option(FAST_BUILD "Sets options to speed up build/reduce obj/executable size" OFF)
|
option(FAST_BUILD "Sets options to speed up build/reduce obj/executable size" OFF)
|
||||||
|
option(WIL_BUILD_PACKAGING "Sets option to build the packaging, default on" ON)
|
||||||
|
option(WIL_BUILD_TESTS "Sets option to build the unit tests, default on" ON)
|
||||||
|
|
||||||
if (NOT DEFINED WIL_BUILD_VERSION)
|
if (NOT DEFINED WIL_BUILD_VERSION)
|
||||||
set(WIL_BUILD_VERSION "0.0.0")
|
set(WIL_BUILD_VERSION "0.0.0")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (NOT DEFINED CPPWINRT_VERSION)
|
||||||
|
set(CPPWINRT_VERSION "2.0.221121.5")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Detect the Windows SDK version. If we're using the Visual Studio generator, this will be provided for us. Otherwise
|
# Detect the Windows SDK version. If we're using the Visual Studio generator, this will be provided for us. Otherwise
|
||||||
# we'll need to assume that this value comes from the command line (e.g. through the VS command prompt)
|
# we'll need to assume that this value comes from the command line (e.g. through the VS command prompt)
|
||||||
if (DEFINED CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION)
|
if (DEFINED CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION)
|
||||||
|
@ -17,5 +25,39 @@ else()
|
||||||
string(REGEX REPLACE "\\\\$" "" WIL_WINDOWS_SDK_VERSION "$ENV{WindowsSDKVersion}")
|
string(REGEX REPLACE "\\\\$" "" WIL_WINDOWS_SDK_VERSION "$ENV{WindowsSDKVersion}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(packaging)
|
if (${WIL_BUILD_PACKAGING})
|
||||||
add_subdirectory(tests)
|
add_subdirectory(packaging)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (${WIL_BUILD_TESTS})
|
||||||
|
add_subdirectory(tests)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Gather headers into an interface library.
|
||||||
|
file(GLOB_RECURSE HEADER_FILES "${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME}/*.h")
|
||||||
|
add_library(${PROJECT_NAME} INTERFACE)
|
||||||
|
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
||||||
|
|
||||||
|
# The interface's include directory.
|
||||||
|
target_include_directories(${PROJECT_NAME} INTERFACE
|
||||||
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||||
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||||
|
)
|
||||||
|
|
||||||
|
# Include the .natvis files
|
||||||
|
if (MSVC)
|
||||||
|
target_sources(${PROJECT_NAME} INTERFACE
|
||||||
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/natvis/wil.natvis>")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Install Package Configuration
|
||||||
|
string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWER)
|
||||||
|
install(TARGETS ${PROJECT_NAME} EXPORT ${PROJECT_NAME_LOWER}_targets)
|
||||||
|
install(EXPORT ${PROJECT_NAME_LOWER}_targets
|
||||||
|
NAMESPACE ${PROJECT_NAME}::
|
||||||
|
FILE ${PROJECT_NAME_LOWER}Config.cmake
|
||||||
|
DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${PROJECT_NAME}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Install the headers at a standard cmake location.
|
||||||
|
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/wil" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
|
|
|
@ -12,7 +12,8 @@ Some things that WIL includes to whet your appetite:
|
||||||
Smart pointers and auto-releasing resource wrappers to let you manage Windows
|
Smart pointers and auto-releasing resource wrappers to let you manage Windows
|
||||||
API HANDLEs, HWNDs, and other resources and resource handles with
|
API HANDLEs, HWNDs, and other resources and resource handles with
|
||||||
[RAII](https://en.cppreference.com/w/cpp/language/raii) semantics.
|
[RAII](https://en.cppreference.com/w/cpp/language/raii) semantics.
|
||||||
- [`include/wil/win32_helpers.h`](include/wil/win32_helpers.h): Wrappers for API functions
|
- [`include/wil/win32_helpers.h`](include/wil/win32_helpers.h)
|
||||||
|
([documentation](https://github.com/microsoft/wil/wiki/Win32-helpers)): Wrappers for API functions
|
||||||
that save you the work of manually specifying buffer sizes, calling a function twice
|
that save you the work of manually specifying buffer sizes, calling a function twice
|
||||||
to get the needed buffer size and then allocate and pass the right-size buffer,
|
to get the needed buffer size and then allocate and pass the right-size buffer,
|
||||||
casting or converting between types, and so on.
|
casting or converting between types, and so on.
|
||||||
|
@ -24,6 +25,11 @@ Some things that WIL includes to whet your appetite:
|
||||||
Preprocessor macros to help you check for errors from Windows API functions,
|
Preprocessor macros to help you check for errors from Windows API functions,
|
||||||
in many of the myriad ways those errors are reported, and surface them as
|
in many of the myriad ways those errors are reported, and surface them as
|
||||||
error codes or C++ exceptions in your code.
|
error codes or C++ exceptions in your code.
|
||||||
|
- [`include/wil/Tracelogging.h`](include/wil/Tracelogging.h): This file contains the convenience macros
|
||||||
|
that enable developers define and log telemetry. These macros use
|
||||||
|
[`TraceLogging API`](https://docs.microsoft.com/en-us/windows/win32/tracelogging/trace-logging-portal)
|
||||||
|
to log data. This data can be viewed in tools such as
|
||||||
|
[`Windows Performance Analyzer`](https://docs.microsoft.com/en-us/windows-hardware/test/wpt/windows-performance-analyzer).
|
||||||
|
|
||||||
WIL can be used by C++ code that uses C++ exceptions as well as code that uses returned
|
WIL can be used by C++ code that uses C++ exceptions as well as code that uses returned
|
||||||
error codes to report errors. All of WIL can be used from user-space Windows code,
|
error codes to report errors. All of WIL can be used from user-space Windows code,
|
||||||
|
@ -50,7 +56,7 @@ Note that even though WIL is a header-only library, you still need to install th
|
||||||
# Building/Testing
|
# Building/Testing
|
||||||
To get started testing WIL, first make sure that you have a recent version of [Visual Studio](https://visualstudio.microsoft.com/downloads/) and the most recent [Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk) installed. If you are doing
|
To get started testing WIL, first make sure that you have a recent version of [Visual Studio](https://visualstudio.microsoft.com/downloads/) and the most recent [Windows SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk) installed. If you are doing
|
||||||
any non-trivial work, also be sure to have a recent version of [Clang](http://releases.llvm.org/download.html) installed. Once everything is installed, open a VS
|
any non-trivial work, also be sure to have a recent version of [Clang](http://releases.llvm.org/download.html) installed. Once everything is installed, open a VS
|
||||||
native command window (e.g. "x64 Native Tools Command Prompt for VS 2019"). If you are familiar with CMake you can get started building normally. Otherwise, or if you prefer to skip all of the boilerplate, you can use one of the scripts in the [scripts](scripts) directory:
|
native command window (e.g. "x64 Native Tools Command Prompt for VS 2022"). If you are familiar with CMake you can get started building normally. Otherwise, or if you prefer to skip all of the boilerplate, you can use one of the scripts in the [scripts](scripts) directory:
|
||||||
```cmd
|
```cmd
|
||||||
C:\wil> scripts\init.cmd -c clang -g ninja -b debug
|
C:\wil> scripts\init.cmd -c clang -g ninja -b debug
|
||||||
```
|
```
|
||||||
|
@ -76,7 +82,7 @@ C:\wil> scripts\build_all.cmd
|
||||||
C:\wil> scripts\runtests.cmd
|
C:\wil> scripts\runtests.cmd
|
||||||
```
|
```
|
||||||
Note that this will only test for the architecture that corresponds to the command window you opened. You will want to
|
Note that this will only test for the architecture that corresponds to the command window you opened. You will want to
|
||||||
repeat this process for the other architecture (e.g. by using the "x86 Native Tools Command Prompt for VS 2019")
|
repeat this process for the other architecture (e.g. by using the "x86 Native Tools Command Prompt for VS 2022")
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
|
|
||||||
# E.g. replace_cxx_flag("/W[0-4]", "/W4")
|
# This is unfortunately still needed to disable exceptions/RTTI since modern CMake still has no builtin support...
|
||||||
|
# E.g. replace_cxx_flag("/EHsc", "/EHs-c-")
|
||||||
macro(replace_cxx_flag pattern text)
|
macro(replace_cxx_flag pattern text)
|
||||||
foreach (flag
|
foreach (flag
|
||||||
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
|
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
|
||||||
|
@ -10,60 +11,61 @@ macro(replace_cxx_flag pattern text)
|
||||||
endforeach()
|
endforeach()
|
||||||
endmacro()
|
endmacro()
|
||||||
|
|
||||||
macro(append_cxx_flag text)
|
|
||||||
foreach (flag
|
|
||||||
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
|
|
||||||
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
|
||||||
|
|
||||||
string(APPEND ${flag} " ${text}")
|
|
||||||
|
|
||||||
endforeach()
|
|
||||||
endmacro()
|
|
||||||
|
|
||||||
# Fixup default compiler settings
|
# Fixup default compiler settings
|
||||||
|
add_compile_options(
|
||||||
|
# Be as strict as reasonably possible, since we want to support consumers using strict warning levels
|
||||||
|
/W4 /WX
|
||||||
|
)
|
||||||
|
|
||||||
# Be as strict as reasonably possible, since we want to support consumers using strict warning levels
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
replace_cxx_flag("/W[0-4]" "/W4")
|
add_compile_options(
|
||||||
append_cxx_flag("/WX")
|
# Ignore some pedantic warnings enabled by '-Wextra'
|
||||||
|
-Wno-missing-field-initializers
|
||||||
|
|
||||||
# We want to be as conformant as possible, so tell MSVC to not be permissive (note that this has no effect on clang-cl)
|
# Ignore some pedantic warnings enabled by '-Wpedantic'
|
||||||
append_cxx_flag("/permissive-")
|
-Wno-language-extension-token
|
||||||
|
-Wno-c++17-attribute-extensions
|
||||||
# wistd::function has padding due to alignment. This is expected
|
-Wno-gnu-zero-variadic-macro-arguments
|
||||||
append_cxx_flag("/wd4324")
|
-Wno-extra-semi
|
||||||
|
|
||||||
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
|
|
||||||
# Ignore a few Clang warnings. We may want to revisit in the future to see if any of these can/should be removed
|
|
||||||
append_cxx_flag("-Wno-switch")
|
|
||||||
append_cxx_flag("-Wno-c++17-compat-mangling")
|
|
||||||
append_cxx_flag("-Wno-missing-field-initializers")
|
|
||||||
|
|
||||||
# For tests, we want to be able to test self assignment, so disable this warning
|
# For tests, we want to be able to test self assignment, so disable this warning
|
||||||
append_cxx_flag("-Wno-self-assign-overloaded")
|
-Wno-self-assign-overloaded
|
||||||
append_cxx_flag("-Wno-self-move")
|
-Wno-self-move
|
||||||
|
|
||||||
# clang-cl does not understand the /permissive- flag (or at least it opts to ignore it). We can achieve similar
|
# clang needs this to enable _InterlockedCompareExchange128
|
||||||
# results through the following flags.
|
-mcx16
|
||||||
append_cxx_flag("-fno-delayed-template-parsing")
|
|
||||||
|
# We don't want legacy MSVC conformance
|
||||||
|
-fno-delayed-template-parsing
|
||||||
|
|
||||||
# NOTE: Windows headers not clean enough for us to realistically attempt to start fixing these errors yet. That
|
# NOTE: Windows headers not clean enough for us to realistically attempt to start fixing these errors yet. That
|
||||||
# said, errors that originate from WIL headers may benefit
|
# said, errors that originate from WIL headers may benefit
|
||||||
# append_cxx_flag("-fno-ms-compatibility")
|
# -fno-ms-compatibility
|
||||||
# append_cxx_flag("-ferror-limit=999")
|
# -ferror-limit=999
|
||||||
# append_cxx_flag("-fmacro-backtrace-limit=0")
|
# -fmacro-backtrace-limit=0
|
||||||
# -fno-ms-compatibility turns off preprocessor compatability, which currently only works when __VA_OPT__ support is
|
|
||||||
# available (i.e. >= C++20)
|
# -fno-ms-compatibility turns off preprocessor compatability, which currently only works when __VA_OPT__ support
|
||||||
# append_cxx_flag("-Xclang -std=c++2a")
|
# is available (i.e. >= C++20)
|
||||||
|
# -Xclang -std=c++2a
|
||||||
|
)
|
||||||
else()
|
else()
|
||||||
# Flags that are either ignored or unrecognized by clang-cl
|
add_compile_options(
|
||||||
|
# We want to be as conformant as possible, so tell MSVC to not be permissive (note that this has no effect on clang-cl)
|
||||||
|
/permissive-
|
||||||
|
|
||||||
|
# wistd::function has padding due to alignment. This is expected
|
||||||
|
/wd4324
|
||||||
|
|
||||||
# TODO: https://github.com/Microsoft/wil/issues/6
|
# TODO: https://github.com/Microsoft/wil/issues/6
|
||||||
# append_cxx_flag("/experimental:preprocessor")
|
# /experimental:preprocessor
|
||||||
|
|
||||||
# CRT headers are not yet /experimental:preprocessor clean, so work around the known issues
|
# CRT headers are not yet /experimental:preprocessor clean, so work around the known issues
|
||||||
# append_cxx_flag("/Wv:18")
|
# /Wv:18
|
||||||
|
|
||||||
append_cxx_flag("/bigobj")
|
# Some tests have a LOT of template instantiations
|
||||||
|
/bigobj
|
||||||
|
|
||||||
# NOTE: Temporary workaround while https://github.com/microsoft/wil/issues/102 is being investigated
|
# NOTE: Temporary workaround while https://github.com/microsoft/wil/issues/102 is being investigated
|
||||||
append_cxx_flag("/d2FH4-")
|
/d2FH4-
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -16,6 +16,13 @@
|
||||||
#include "result.h"
|
#include "result.h"
|
||||||
#include "resource.h" // last to ensure _COMBASEAPI_H_ protected definitions are available
|
#include "resource.h" // last to ensure _COMBASEAPI_H_ protected definitions are available
|
||||||
|
|
||||||
|
#if __has_include(<tuple>)
|
||||||
|
#include <tuple>
|
||||||
|
#endif
|
||||||
|
#if __has_include(<type_traits>)
|
||||||
|
#include <type_traits>
|
||||||
|
#endif
|
||||||
|
|
||||||
// Forward declaration within WIL (see https://msdn.microsoft.com/en-us/library/br244983.aspx)
|
// Forward declaration within WIL (see https://msdn.microsoft.com/en-us/library/br244983.aspx)
|
||||||
/// @cond
|
/// @cond
|
||||||
namespace Microsoft
|
namespace Microsoft
|
||||||
|
@ -46,10 +53,10 @@ namespace wil
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef wistd::integral_constant<char, 0> tag_com_query;
|
using tag_com_query = wistd::integral_constant<char, 0>;
|
||||||
typedef wistd::integral_constant<char, 1> tag_try_com_query;
|
using tag_try_com_query = wistd::integral_constant<char, 1>;
|
||||||
typedef wistd::integral_constant<char, 2> tag_com_copy;
|
using tag_com_copy = wistd::integral_constant<char, 2>;
|
||||||
typedef wistd::integral_constant<char, 3> tag_try_com_copy;
|
using tag_try_com_copy = wistd::integral_constant<char, 3>;
|
||||||
|
|
||||||
class default_query_policy
|
class default_query_policy
|
||||||
{
|
{
|
||||||
|
@ -87,7 +94,7 @@ namespace wil
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct query_policy_helper
|
struct query_policy_helper
|
||||||
{
|
{
|
||||||
typedef default_query_policy type;
|
using type = default_query_policy;
|
||||||
};
|
};
|
||||||
|
|
||||||
class weak_query_policy
|
class weak_query_policy
|
||||||
|
@ -99,7 +106,7 @@ namespace wil
|
||||||
*result = nullptr;
|
*result = nullptr;
|
||||||
|
|
||||||
IInspectable* temp;
|
IInspectable* temp;
|
||||||
HRESULT hr = ptr->Resolve(__uuidof(IInspectable), reinterpret_cast<IInspectable**>(&temp));
|
HRESULT hr = ptr->Resolve(__uuidof(IInspectable), &temp);
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
if (temp == nullptr)
|
if (temp == nullptr)
|
||||||
|
@ -144,7 +151,7 @@ namespace wil
|
||||||
template <>
|
template <>
|
||||||
struct query_policy_helper<IWeakReference>
|
struct query_policy_helper<IWeakReference>
|
||||||
{
|
{
|
||||||
typedef weak_query_policy type;
|
using type = weak_query_policy;
|
||||||
};
|
};
|
||||||
|
|
||||||
#if (NTDDI_VERSION >= NTDDI_WINBLUE)
|
#if (NTDDI_VERSION >= NTDDI_WINBLUE)
|
||||||
|
@ -170,7 +177,7 @@ namespace wil
|
||||||
template <>
|
template <>
|
||||||
struct query_policy_helper<IAgileReference>
|
struct query_policy_helper<IAgileReference>
|
||||||
{
|
{
|
||||||
typedef agile_query_policy type;
|
using type = agile_query_policy;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -190,15 +197,15 @@ namespace wil
|
||||||
class com_ptr_t
|
class com_ptr_t
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
typedef typename wistd::add_lvalue_reference<T>::type element_type_reference;
|
using element_type_reference = typename wistd::add_lvalue_reference<T>::type;
|
||||||
typedef details::query_policy_t<T> query_policy;
|
using query_policy = details::query_policy_t<T>;
|
||||||
public:
|
public:
|
||||||
//! The function return result (HRESULT or void) for the given err_policy (see @ref page_errors).
|
//! The function return result (HRESULT or void) for the given err_policy (see @ref page_errors).
|
||||||
typedef typename err_policy::result result;
|
using result = typename err_policy::result;
|
||||||
//! The template type `T` being held by the com_ptr_t.
|
//! The template type `T` being held by the com_ptr_t.
|
||||||
typedef T element_type;
|
using element_type = T;
|
||||||
//! A pointer to the template type `T` being held by the com_ptr_t (what `get()` returns).
|
//! A pointer to the template type `T` being held by the com_ptr_t (what `get()` returns).
|
||||||
typedef T* pointer;
|
using pointer = T*;
|
||||||
|
|
||||||
//! @name Constructors
|
//! @name Constructors
|
||||||
//! @{
|
//! @{
|
||||||
|
@ -359,8 +366,7 @@ namespace wil
|
||||||
m_ptr = other;
|
m_ptr = other;
|
||||||
if (ptr)
|
if (ptr)
|
||||||
{
|
{
|
||||||
ULONG ref;
|
ULONG ref = ptr->Release();
|
||||||
ref = ptr->Release();
|
|
||||||
WI_ASSERT_MSG(((other != ptr) || (ref > 0)), "Bug: Attaching the same already assigned, destructed pointer");
|
WI_ASSERT_MSG(((other != ptr) || (ref > 0)), "Bug: Attaching the same already assigned, destructed pointer");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -397,9 +403,9 @@ namespace wil
|
||||||
|
|
||||||
//! Returns the address of the internal pointer casted to IUnknown** (releases ownership of the pointer BEFORE returning the address).
|
//! Returns the address of the internal pointer casted to IUnknown** (releases ownership of the pointer BEFORE returning the address).
|
||||||
//! @see put
|
//! @see put
|
||||||
IUnknown** put_unknown() WI_NOEXCEPT
|
::IUnknown** put_unknown() WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
return reinterpret_cast<IUnknown**>(put());
|
return reinterpret_cast<::IUnknown**>(put());
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Returns the address of the internal pointer (releases ownership of the pointer BEFORE returning the address).
|
//! Returns the address of the internal pointer (releases ownership of the pointer BEFORE returning the address).
|
||||||
|
@ -863,7 +869,7 @@ namespace wil
|
||||||
// Internal Helpers
|
// Internal Helpers
|
||||||
/// @cond
|
/// @cond
|
||||||
template <class U>
|
template <class U>
|
||||||
inline com_ptr_t(_In_ U* ptr, details::tag_com_query)
|
inline com_ptr_t(_In_ U* ptr, details::tag_com_query) : m_ptr(nullptr)
|
||||||
{
|
{
|
||||||
err_policy::HResult(details::query_policy_t<U>::query(ptr, &m_ptr));
|
err_policy::HResult(details::query_policy_t<U>::query(ptr, &m_ptr));
|
||||||
}
|
}
|
||||||
|
@ -875,14 +881,12 @@ namespace wil
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class U>
|
template <class U>
|
||||||
inline com_ptr_t(_In_opt_ U* ptr, details::tag_com_copy)
|
inline com_ptr_t(_In_opt_ U* ptr, details::tag_com_copy) : m_ptr(nullptr)
|
||||||
{
|
{
|
||||||
if (ptr)
|
if (ptr)
|
||||||
{
|
{
|
||||||
err_policy::HResult(details::query_policy_t<U>::query(ptr, &m_ptr));
|
err_policy::HResult(details::query_policy_t<U>::query(ptr, &m_ptr));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
m_ptr = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class U>
|
template <class U>
|
||||||
|
@ -1182,6 +1186,43 @@ namespace wil
|
||||||
#endif
|
#endif
|
||||||
/// @endcond
|
/// @endcond
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
//! Constructs a `com_ptr` from a raw pointer.
|
||||||
|
//! This avoids having to restate the interface in pre-C++20.
|
||||||
|
//! Starting in C++20, you can write `wil::com_ptr(p)` directly.
|
||||||
|
//! ~~~
|
||||||
|
//! void example(ILongNamedThing* thing)
|
||||||
|
//! {
|
||||||
|
//! callback([thing = wil::make_com_ptr(thing)] { /* do something */ });
|
||||||
|
//! }
|
||||||
|
//! ~~~
|
||||||
|
template <typename T>
|
||||||
|
com_ptr<T> make_com_ptr(T* p) { return p; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//! Constructs a `com_ptr_nothrow` from a raw pointer.
|
||||||
|
//! This avoids having to restate the interface in pre-C++20.
|
||||||
|
//! Starting in C++20, you can write `wil::com_ptr_nothrow(p)` directly.
|
||||||
|
//! ~~~
|
||||||
|
//! void example(ILongNamedThing* thing)
|
||||||
|
//! {
|
||||||
|
//! callback([thing = wil::make_com_ptr_nothrow(thing)] { /* do something */ });
|
||||||
|
//! }
|
||||||
|
//! ~~~
|
||||||
|
template <typename T>
|
||||||
|
com_ptr_nothrow<T> make_com_ptr_nothrow(T* p) { return p; }
|
||||||
|
|
||||||
|
//! Constructs a `com_ptr_failfast` from a raw pointer.
|
||||||
|
//! This avoids having to restate the interface in pre-C++20.
|
||||||
|
//! Starting in C++20, you can write `wil::com_ptr_failfast(p)` directly.
|
||||||
|
//! ~~~
|
||||||
|
//! void example(ILongNamedThing* thing)
|
||||||
|
//! {
|
||||||
|
//! callback([thing = wil::make_com_ptr_failfast(thing)] { /* do something */ });
|
||||||
|
//! }
|
||||||
|
//! ~~~
|
||||||
|
template <typename T>
|
||||||
|
com_ptr_failfast<T> make_com_ptr_failfast(T* p) { return p; }
|
||||||
|
|
||||||
//! @name Stand-alone query helpers
|
//! @name Stand-alone query helpers
|
||||||
//! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr
|
//! * Source pointer can be raw interface pointer, any wil com_ptr, or WRL ComPtr
|
||||||
|
@ -1255,7 +1296,7 @@ namespace wil
|
||||||
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
||||||
auto hr = details::query_policy_t<decltype(raw)>::query(raw, ptrResult);
|
auto hr = details::query_policy_t<decltype(raw)>::query(raw, ptrResult);
|
||||||
__analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr));
|
__analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr));
|
||||||
RETURN_HR(hr);
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
@ -1297,7 +1338,7 @@ namespace wil
|
||||||
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
||||||
auto hr = details::query_policy_t<decltype(raw)>::query(raw, riid, ptrResult);
|
auto hr = details::query_policy_t<decltype(raw)>::query(raw, riid, ptrResult);
|
||||||
__analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr));
|
__analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr));
|
||||||
RETURN_HR(hr);
|
return hr;
|
||||||
}
|
}
|
||||||
//! @}
|
//! @}
|
||||||
|
|
||||||
|
@ -1687,7 +1728,7 @@ namespace wil
|
||||||
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
||||||
auto hr = ::RoGetAgileReference(options, __uuidof(raw), raw, ptrResult);
|
auto hr = ::RoGetAgileReference(options, __uuidof(raw), raw, ptrResult);
|
||||||
__analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr));
|
__analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr));
|
||||||
RETURN_HR(hr);
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
@ -1802,7 +1843,7 @@ namespace wil
|
||||||
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
auto raw = com_raw_ptr(wistd::forward<T>(ptrSource));
|
||||||
auto hr = details::GetWeakReference(raw, ptrResult);
|
auto hr = details::GetWeakReference(raw, ptrResult);
|
||||||
__analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr));
|
__analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr));
|
||||||
RETURN_HR(hr);
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
@ -1873,14 +1914,14 @@ namespace wil
|
||||||
|
|
||||||
/** constructs a COM object using an CLSID on a specific interface or IUnknown. */
|
/** constructs a COM object using an CLSID on a specific interface or IUnknown. */
|
||||||
template<typename Interface = IUnknown>
|
template<typename Interface = IUnknown>
|
||||||
wil::com_ptr_failfast<Interface> CoCreateInstanceFailFast(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER)
|
wil::com_ptr_failfast<Interface> CoCreateInstanceFailFast(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
return CoCreateInstance<Interface, err_failfast_policy>(rclsid, dwClsContext);
|
return CoCreateInstance<Interface, err_failfast_policy>(rclsid, dwClsContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** constructs a COM object using the class as the identifier (that has an associated CLSID) on a specific interface or IUnknown. */
|
/** constructs a COM object using the class as the identifier (that has an associated CLSID) on a specific interface or IUnknown. */
|
||||||
template<typename Class, typename Interface = IUnknown>
|
template<typename Class, typename Interface = IUnknown>
|
||||||
wil::com_ptr_failfast<Interface> CoCreateInstanceFailFast(DWORD dwClsContext = CLSCTX_INPROC_SERVER)
|
wil::com_ptr_failfast<Interface> CoCreateInstanceFailFast(DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
return CoCreateInstanceFailFast<Interface>(__uuidof(Class), dwClsContext);
|
return CoCreateInstanceFailFast<Interface>(__uuidof(Class), dwClsContext);
|
||||||
}
|
}
|
||||||
|
@ -1888,7 +1929,7 @@ namespace wil
|
||||||
/** constructs a COM object using an CLSID on a specific interface or IUnknown.
|
/** constructs a COM object using an CLSID on a specific interface or IUnknown.
|
||||||
Note, failures are reported as a null result, the HRESULT is lost. */
|
Note, failures are reported as a null result, the HRESULT is lost. */
|
||||||
template<typename Interface = IUnknown>
|
template<typename Interface = IUnknown>
|
||||||
wil::com_ptr_nothrow<Interface> CoCreateInstanceNoThrow(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER)
|
wil::com_ptr_nothrow<Interface> CoCreateInstanceNoThrow(REFCLSID rclsid, DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
return CoCreateInstance<Interface, err_returncode_policy>(rclsid, dwClsContext);
|
return CoCreateInstance<Interface, err_returncode_policy>(rclsid, dwClsContext);
|
||||||
}
|
}
|
||||||
|
@ -1896,7 +1937,7 @@ namespace wil
|
||||||
/** constructs a COM object using the class as the identifier (that has an associated CLSID) on a specific interface or IUnknown.
|
/** constructs a COM object using the class as the identifier (that has an associated CLSID) on a specific interface or IUnknown.
|
||||||
Note, failures are reported as a null result, the HRESULT is lost. */
|
Note, failures are reported as a null result, the HRESULT is lost. */
|
||||||
template<typename Class, typename Interface = IUnknown>
|
template<typename Class, typename Interface = IUnknown>
|
||||||
wil::com_ptr_nothrow<Interface> CoCreateInstanceNoThrow(DWORD dwClsContext = CLSCTX_INPROC_SERVER)
|
wil::com_ptr_nothrow<Interface> CoCreateInstanceNoThrow(DWORD dwClsContext = CLSCTX_INPROC_SERVER) WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
return CoCreateInstanceNoThrow<Interface>(__uuidof(Class), dwClsContext);
|
return CoCreateInstanceNoThrow<Interface>(__uuidof(Class), dwClsContext);
|
||||||
}
|
}
|
||||||
|
@ -1949,6 +1990,140 @@ namespace wil
|
||||||
{
|
{
|
||||||
return CoGetClassObjectNoThrow<Interface>(__uuidof(Class), dwClsContext);
|
return CoGetClassObjectNoThrow<Interface>(__uuidof(Class), dwClsContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if __cpp_lib_apply && __has_include(<type_traits>)
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
template <typename error_policy, typename... Results>
|
||||||
|
auto CoCreateInstanceEx(REFCLSID clsid, CLSCTX clsCtx) noexcept
|
||||||
|
{
|
||||||
|
MULTI_QI multiQis[sizeof...(Results)]{};
|
||||||
|
const IID* iids[sizeof...(Results)]{ &__uuidof(Results)... };
|
||||||
|
|
||||||
|
static_assert(sizeof...(Results) > 0);
|
||||||
|
|
||||||
|
for (auto i = 0U; i < sizeof...(Results); ++i)
|
||||||
|
{
|
||||||
|
multiQis[i].pIID = iids[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto hr = CoCreateInstanceEx(clsid, nullptr, clsCtx, nullptr,
|
||||||
|
ARRAYSIZE(multiQis), multiQis);
|
||||||
|
|
||||||
|
std::tuple<wil::com_ptr_t<Results, error_policy>...> resultTuple;
|
||||||
|
|
||||||
|
std::apply([i = 0, &multiQis](auto&... a) mutable
|
||||||
|
{
|
||||||
|
(a.attach(reinterpret_cast<typename std::remove_reference<decltype(a)>::type::pointer>(multiQis[i++].pItf)), ...);
|
||||||
|
}, resultTuple);
|
||||||
|
return std::tuple<HRESULT, decltype(resultTuple)>(hr, std::move(resultTuple));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename error_policy, typename... Results>
|
||||||
|
auto com_multi_query(IUnknown* obj)
|
||||||
|
{
|
||||||
|
MULTI_QI multiQis[sizeof...(Results)]{};
|
||||||
|
const IID* iids[sizeof...(Results)]{ &__uuidof(Results)... };
|
||||||
|
|
||||||
|
static_assert(sizeof...(Results) > 0);
|
||||||
|
|
||||||
|
for (auto i = 0U; i < sizeof...(Results); ++i)
|
||||||
|
{
|
||||||
|
multiQis[i].pIID = iids[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<wil::com_ptr_t<Results, error_policy>...> resultTuple{};
|
||||||
|
|
||||||
|
wil::com_ptr_nothrow<IMultiQI> multiQi;
|
||||||
|
auto hr = obj->QueryInterface(IID_PPV_ARGS(&multiQi));
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
hr = multiQi->QueryMultipleInterfaces(ARRAYSIZE(multiQis), multiQis);
|
||||||
|
std::apply([i = 0, &multiQis](auto&... a) mutable
|
||||||
|
{
|
||||||
|
(a.attach(reinterpret_cast<typename std::remove_reference<decltype(a)>::type::pointer>(multiQis[i++].pItf)), ...);
|
||||||
|
}, resultTuple);
|
||||||
|
}
|
||||||
|
return std::tuple<HRESULT, decltype(resultTuple)>{hr, std::move(resultTuple)};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
// CoCreateInstanceEx can be used to improve performance by requesting multiple interfaces
|
||||||
|
// from an object at create time. This is most useful for out of process (OOP) servers, saving
|
||||||
|
// and RPC per extra interface requested.
|
||||||
|
template <typename... Results>
|
||||||
|
auto CoCreateInstanceEx(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER)
|
||||||
|
{
|
||||||
|
auto [error, result] = details::CoCreateInstanceEx<err_exception_policy, Results...>(clsid, clsCtx);
|
||||||
|
THROW_IF_FAILED(error);
|
||||||
|
THROW_HR_IF(E_NOINTERFACE, error == CO_S_NOTALLINTERFACES);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Results>
|
||||||
|
auto TryCoCreateInstanceEx(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER)
|
||||||
|
{
|
||||||
|
auto [error, result] = details::CoCreateInstanceEx<err_exception_policy, Results...>(clsid, clsCtx);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Returns [error, result] where result is a tuple with each of the requested interfaces.
|
||||||
|
template <typename... Results>
|
||||||
|
auto CoCreateInstanceExNoThrow(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) noexcept
|
||||||
|
{
|
||||||
|
auto [error, result] = details::CoCreateInstanceEx<err_returncode_policy, Results...>(clsid, clsCtx);
|
||||||
|
if (SUCCEEDED(error) && (error == CO_S_NOTALLINTERFACES))
|
||||||
|
{
|
||||||
|
return std::tuple<HRESULT, decltype(result)>{E_NOINTERFACE, {}};
|
||||||
|
}
|
||||||
|
return std::tuple<HRESULT, decltype(result)>{error, result};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Results>
|
||||||
|
auto TryCoCreateInstanceExNoThrow(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) noexcept
|
||||||
|
{
|
||||||
|
auto [error, result] = details::CoCreateInstanceEx<err_returncode_policy, Results...>(clsid, clsCtx);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Results>
|
||||||
|
auto CoCreateInstanceExFailFast(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) noexcept
|
||||||
|
{
|
||||||
|
auto [error, result] = details::CoCreateInstanceEx<err_failfast_policy, Results...>(clsid, clsCtx);
|
||||||
|
FAIL_FAST_IF_FAILED(error);
|
||||||
|
FAIL_FAST_HR_IF(E_NOINTERFACE, error == CO_S_NOTALLINTERFACES);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Results>
|
||||||
|
auto TryCoCreateInstanceExFailFast(REFCLSID clsid, CLSCTX clsCtx = CLSCTX_LOCAL_SERVER) noexcept
|
||||||
|
{
|
||||||
|
auto [error, result] = details::CoCreateInstanceEx<err_failfast_policy, Results...>(clsid, clsCtx);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
template<typename... Results>
|
||||||
|
auto com_multi_query(IUnknown* obj)
|
||||||
|
{
|
||||||
|
auto [error, result] = details::com_multi_query<err_exception_policy, Results...>(obj);
|
||||||
|
THROW_IF_FAILED(error);
|
||||||
|
THROW_HR_IF(E_NOINTERFACE, error == S_FALSE);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Results>
|
||||||
|
auto try_com_multi_query(IUnknown* obj)
|
||||||
|
{
|
||||||
|
auto [error, result] = details::com_multi_query<err_exception_policy, Results...>(obj);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // __cpp_lib_apply && __has_include(<type_traits>)
|
||||||
|
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region Stream helpers
|
#pragma region Stream helpers
|
||||||
|
@ -2601,8 +2776,8 @@ namespace wil
|
||||||
wil::stream_write_string(target, L"Waffles", 3);
|
wil::stream_write_string(target, L"Waffles", 3);
|
||||||
~~~~
|
~~~~
|
||||||
@param target The stream to which to write a string
|
@param target The stream to which to write a string
|
||||||
@param source The string to write. Can be null if `writeLength` is zero
|
@param source The string to write. Can be null if `toWriteCch` is zero
|
||||||
@param writeLength The number of characters to write from source into `target`
|
@param toWriteCch The number of characters to write from source into `target`
|
||||||
*/
|
*/
|
||||||
inline void stream_write_string(_In_ ISequentialStream* target, _In_reads_opt_(toWriteCch) const wchar_t* source, _In_ size_t toWriteCch)
|
inline void stream_write_string(_In_ ISequentialStream* target, _In_reads_opt_(toWriteCch) const wchar_t* source, _In_ size_t toWriteCch)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,467 @@
|
||||||
|
//*********************************************************
|
||||||
|
//
|
||||||
|
// 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_COM_APARTMENT_VARIABLE_INCLUDED
|
||||||
|
#define __WIL_COM_APARTMENT_VARIABLE_INCLUDED
|
||||||
|
|
||||||
|
#include <any>
|
||||||
|
#include <objidl.h>
|
||||||
|
#include <roapi.h>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <winrt/Windows.Foundation.h>
|
||||||
|
|
||||||
|
#include "com.h"
|
||||||
|
#include "cppwinrt.h"
|
||||||
|
#include "result_macros.h"
|
||||||
|
#include "win32_helpers.h"
|
||||||
|
|
||||||
|
#ifndef WIL_ENABLE_EXCEPTIONS
|
||||||
|
#error This header requires exceptions
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace wil
|
||||||
|
{
|
||||||
|
// Determine if apartment variables are supported in the current process context.
|
||||||
|
// Prior to build 22365, the APIs needed to create apartment variables (e.g. RoGetApartmentIdentifier)
|
||||||
|
// failed for unpackaged processes. For MS people, see http://task.ms/31861017 for details.
|
||||||
|
// APIs needed to implement apartment variables did not work in non-packaged processes.
|
||||||
|
inline bool are_apartment_variables_supported()
|
||||||
|
{
|
||||||
|
unsigned long long apartmentId{};
|
||||||
|
return RoGetApartmentIdentifier(&apartmentId) != HRESULT_FROM_WIN32(ERROR_API_UNAVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// COM will implicitly rundown the apartment registration when it invokes a handler
|
||||||
|
// and blocks calling unregister when executing the callback. So be careful to release()
|
||||||
|
// this when callback is invoked to avoid a double free of the cookie.
|
||||||
|
using unique_apartment_shutdown_registration = unique_any<APARTMENT_SHUTDOWN_REGISTRATION_COOKIE, decltype(&::RoUnregisterForApartmentShutdown), ::RoUnregisterForApartmentShutdown>;
|
||||||
|
|
||||||
|
struct apartment_variable_platform
|
||||||
|
{
|
||||||
|
static unsigned long long GetApartmentId()
|
||||||
|
{
|
||||||
|
unsigned long long apartmentId{};
|
||||||
|
FAIL_FAST_IF_FAILED(RoGetApartmentIdentifier(&apartmentId));
|
||||||
|
return apartmentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto RegisterForApartmentShutdown(IApartmentShutdown* observer)
|
||||||
|
{
|
||||||
|
unsigned long long id{};
|
||||||
|
shutdown_type cookie;
|
||||||
|
THROW_IF_FAILED(RoRegisterForApartmentShutdown(observer, &id, cookie.put()));
|
||||||
|
return cookie;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UnRegisterForApartmentShutdown(APARTMENT_SHUTDOWN_REGISTRATION_COOKIE cookie)
|
||||||
|
{
|
||||||
|
FAIL_FAST_IF_FAILED(RoUnregisterForApartmentShutdown(cookie));
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto CoInitializeEx(DWORD coinitFlags = 0 /*COINIT_MULTITHREADED*/)
|
||||||
|
{
|
||||||
|
return wil::CoInitializeEx(coinitFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable the test hook
|
||||||
|
inline static constexpr unsigned long AsyncRundownDelayForTestingRaces = INFINITE;
|
||||||
|
|
||||||
|
using shutdown_type = wil::unique_apartment_shutdown_registration;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class apartment_variable_leak_action { fail_fast, ignore };
|
||||||
|
|
||||||
|
// "pins" the current module in memory by incrementing the module reference count and leaking that.
|
||||||
|
inline void ensure_module_stays_loaded()
|
||||||
|
{
|
||||||
|
static INIT_ONCE s_initLeakModule{}; // avoiding magic statics
|
||||||
|
wil::init_once_failfast(s_initLeakModule, []()
|
||||||
|
{
|
||||||
|
HMODULE result{};
|
||||||
|
FAIL_FAST_IF(!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN, L"", &result));
|
||||||
|
return S_OK;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
// For the address of data, you can detect global variables by the ability to resolve the module from the address.
|
||||||
|
inline bool IsGlobalVariable(const void* moduleAddress) noexcept
|
||||||
|
{
|
||||||
|
wil::unique_hmodule moduleHandle;
|
||||||
|
return GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<PCWSTR>(moduleAddress), &moduleHandle) != FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct any_maker_base
|
||||||
|
{
|
||||||
|
std::any(*adapter)(void*);
|
||||||
|
void* inner;
|
||||||
|
|
||||||
|
std::any operator()() const
|
||||||
|
{
|
||||||
|
return adapter(inner);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct any_maker : any_maker_base
|
||||||
|
{
|
||||||
|
any_maker()
|
||||||
|
{
|
||||||
|
adapter = [](auto) -> std::any { return T{}; };
|
||||||
|
}
|
||||||
|
|
||||||
|
any_maker(T(*maker)())
|
||||||
|
{
|
||||||
|
adapter = [](auto maker) -> std::any { return reinterpret_cast<T(*)()>(maker)(); };
|
||||||
|
inner = reinterpret_cast<void*>(maker);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename F>
|
||||||
|
any_maker(F&& f)
|
||||||
|
{
|
||||||
|
adapter = [](auto maker) -> std::any { return reinterpret_cast<F*>(maker)[0](); };
|
||||||
|
inner = std::addressof(f);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<apartment_variable_leak_action leak_action = apartment_variable_leak_action::fail_fast,
|
||||||
|
typename test_hook = apartment_variable_platform>
|
||||||
|
struct apartment_variable_base
|
||||||
|
{
|
||||||
|
inline static winrt::slim_mutex s_lock;
|
||||||
|
|
||||||
|
struct apartment_variable_storage
|
||||||
|
{
|
||||||
|
apartment_variable_storage(apartment_variable_storage&& other) noexcept = default;
|
||||||
|
apartment_variable_storage(const apartment_variable_storage& other) = delete;
|
||||||
|
|
||||||
|
apartment_variable_storage(typename test_hook::shutdown_type&& cookie_) : cookie(std::move(cookie_))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
winrt::apartment_context context;
|
||||||
|
typename test_hook::shutdown_type cookie;
|
||||||
|
// Variables are stored using the address of the apartment_variable_base<> as the key.
|
||||||
|
std::unordered_map<apartment_variable_base<leak_action, test_hook>*, std::any> variables;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Apartment id -> variables storage.
|
||||||
|
inline static wil::object_without_destructor_on_shutdown<
|
||||||
|
std::unordered_map<unsigned long long, apartment_variable_storage>>
|
||||||
|
s_apartmentStorage;
|
||||||
|
|
||||||
|
constexpr apartment_variable_base() = default;
|
||||||
|
~apartment_variable_base()
|
||||||
|
{
|
||||||
|
// Global variables (object with static storage duration)
|
||||||
|
// are run down when the process is shutting down or when the
|
||||||
|
// dll is unloaded. At these points it is not possible to start
|
||||||
|
// an async operation and the work performed is not needed,
|
||||||
|
// the apartments with variable have been run down already.
|
||||||
|
const auto isGlobal = details::IsGlobalVariable(this);
|
||||||
|
if (!isGlobal)
|
||||||
|
{
|
||||||
|
clear_all_apartments_async();
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr (leak_action == apartment_variable_leak_action::fail_fast)
|
||||||
|
{
|
||||||
|
if (isGlobal && !ProcessShutdownInProgress())
|
||||||
|
{
|
||||||
|
// If you hit this fail fast it means the storage in s_apartmentStorage will be leaked.
|
||||||
|
// For apartment variables used in .exes, this is expected and
|
||||||
|
// this fail fast should be disabled using
|
||||||
|
// wil::apartment_variable<T, wil::apartment_variable_leak_action::ignore>
|
||||||
|
//
|
||||||
|
// For DLLs, if this is expected, disable this fail fast using
|
||||||
|
// wil::apartment_variable<T, wil::apartment_variable_leak_action::ignore>
|
||||||
|
//
|
||||||
|
// Use of apartment variables in DLLs only loaded by COM will never hit this case
|
||||||
|
// as COM will unload DLLs before apartments are rundown,
|
||||||
|
// providing the opportunity to empty s_apartmentStorage.
|
||||||
|
//
|
||||||
|
// But DLLs loaded and unloaded to call DLL entry points (outside of COM) may
|
||||||
|
// create variable storage that can't be cleaned up as the DLL lifetime is
|
||||||
|
// shorter that the COM lifetime. In these cases either
|
||||||
|
// 1) accept the leaks and disable the fail fast as describe above
|
||||||
|
// 2) disable module unloading by calling wil::ensure_module_stays_loaded
|
||||||
|
// 3) CoCreate an object from this DLL to make COM aware of the DLL
|
||||||
|
FAIL_FAST_IF(!s_apartmentStorage.get().empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// non-copyable, non-assignable
|
||||||
|
apartment_variable_base(apartment_variable_base const&) = delete;
|
||||||
|
void operator=(apartment_variable_base const&) = delete;
|
||||||
|
|
||||||
|
// get current value or throw if no value has been set
|
||||||
|
std::any& get_existing()
|
||||||
|
{
|
||||||
|
if (auto any = get_if())
|
||||||
|
{
|
||||||
|
return *any;
|
||||||
|
}
|
||||||
|
THROW_HR(E_NOT_SET);
|
||||||
|
}
|
||||||
|
|
||||||
|
static apartment_variable_storage* get_current_apartment_variable_storage()
|
||||||
|
{
|
||||||
|
auto storage = s_apartmentStorage.get().find(test_hook::GetApartmentId());
|
||||||
|
if (storage != s_apartmentStorage.get().end())
|
||||||
|
{
|
||||||
|
return &storage->second;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
apartment_variable_storage* ensure_current_apartment_variables()
|
||||||
|
{
|
||||||
|
auto variables = get_current_apartment_variable_storage();
|
||||||
|
if (variables)
|
||||||
|
{
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ApartmentObserver : public winrt::implements<ApartmentObserver, IApartmentShutdown>
|
||||||
|
{
|
||||||
|
void STDMETHODCALLTYPE OnUninitialize(unsigned long long apartmentId) noexcept override
|
||||||
|
{
|
||||||
|
// This code runs at apartment rundown so be careful to avoid deadlocks by
|
||||||
|
// extracting the variables under the lock then release them outside.
|
||||||
|
auto variables = [apartmentId]()
|
||||||
|
{
|
||||||
|
auto lock = winrt::slim_lock_guard(s_lock);
|
||||||
|
return s_apartmentStorage.get().extract(apartmentId);
|
||||||
|
}();
|
||||||
|
WI_ASSERT(variables.key() == apartmentId);
|
||||||
|
// The system implicitly releases the shutdown observer
|
||||||
|
// after invoking the callback and does not allow calling unregister
|
||||||
|
// in the callback. So release the reference to the registration.
|
||||||
|
variables.mapped().cookie.release();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
auto shutdownRegistration = test_hook::RegisterForApartmentShutdown(winrt::make<ApartmentObserver>().get());
|
||||||
|
return &s_apartmentStorage.get().insert({ test_hook::GetApartmentId(), apartment_variable_storage(std::move(shutdownRegistration)) }).first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get current value or custom-construct one on demand
|
||||||
|
template<typename T>
|
||||||
|
std::any& get_or_create(any_maker<T> && creator)
|
||||||
|
{
|
||||||
|
apartment_variable_storage* variable_storage = nullptr;
|
||||||
|
|
||||||
|
{ // scope for lock
|
||||||
|
auto lock = winrt::slim_lock_guard(s_lock);
|
||||||
|
variable_storage = ensure_current_apartment_variables();
|
||||||
|
|
||||||
|
auto variable = variable_storage->variables.find(this);
|
||||||
|
if (variable != variable_storage->variables.end())
|
||||||
|
{
|
||||||
|
return variable->second;
|
||||||
|
}
|
||||||
|
} // drop the lock
|
||||||
|
|
||||||
|
// create the object outside the lock to avoid reentrant deadlock
|
||||||
|
auto value = creator();
|
||||||
|
|
||||||
|
auto insert_lock = winrt::slim_lock_guard(s_lock);
|
||||||
|
// The insertion may fail if creator() recursively caused itself to be created,
|
||||||
|
// in which case we return the existing object and the falsely-created one is discarded.
|
||||||
|
return variable_storage->variables.insert({ this, std::move(value) }).first->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get pointer to current value or nullptr if no value has been set
|
||||||
|
std::any* get_if()
|
||||||
|
{
|
||||||
|
auto lock = winrt::slim_lock_guard(s_lock);
|
||||||
|
|
||||||
|
if (auto variable_storage = get_current_apartment_variable_storage())
|
||||||
|
{
|
||||||
|
auto variable = variable_storage->variables.find(this);
|
||||||
|
if (variable != variable_storage->variables.end())
|
||||||
|
{
|
||||||
|
return &(variable->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace or create the current value, fail fasts if the value is not already stored
|
||||||
|
void set(std::any value)
|
||||||
|
{
|
||||||
|
// release value, with the swapped value, outside of the lock
|
||||||
|
{
|
||||||
|
auto lock = winrt::slim_lock_guard(s_lock);
|
||||||
|
auto storage = s_apartmentStorage.get().find(test_hook::GetApartmentId());
|
||||||
|
FAIL_FAST_IF(storage == s_apartmentStorage.get().end());
|
||||||
|
auto& variable_storage = storage->second;
|
||||||
|
auto variable = variable_storage.variables.find(this);
|
||||||
|
FAIL_FAST_IF(variable == variable_storage.variables.end());
|
||||||
|
variable->second.swap(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove any current value
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
auto lock = winrt::slim_lock_guard(s_lock);
|
||||||
|
if (auto variable_storage = get_current_apartment_variable_storage())
|
||||||
|
{
|
||||||
|
variable_storage->variables.erase(this);
|
||||||
|
if (variable_storage->variables.size() == 0)
|
||||||
|
{
|
||||||
|
s_apartmentStorage.get().erase(test_hook::GetApartmentId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
winrt::Windows::Foundation::IAsyncAction clear_all_apartments_async()
|
||||||
|
{
|
||||||
|
// gather all the apartments that hold objects we need to destruct
|
||||||
|
// (do not gather the objects themselves, because the apartment might
|
||||||
|
// destruct before we get around to it, and we should let the apartment
|
||||||
|
// destruct the object while it still can).
|
||||||
|
|
||||||
|
std::vector<winrt::apartment_context> contexts;
|
||||||
|
{ // scope for lock
|
||||||
|
auto lock = winrt::slim_lock_guard(s_lock);
|
||||||
|
for (auto& [id, storage] : s_apartmentStorage.get())
|
||||||
|
{
|
||||||
|
auto variable = storage.variables.find(this);
|
||||||
|
if (variable != storage.variables.end())
|
||||||
|
{
|
||||||
|
contexts.push_back(storage.context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contexts.empty())
|
||||||
|
{
|
||||||
|
co_return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wil::unique_mta_usage_cookie mta_reference; // need to extend the MTA due to async cleanup
|
||||||
|
FAIL_FAST_IF_FAILED(CoIncrementMTAUsage(mta_reference.put()));
|
||||||
|
|
||||||
|
// From a background thread hop into each apartment to run down the object
|
||||||
|
// if it's still there.
|
||||||
|
co_await winrt::resume_background();
|
||||||
|
|
||||||
|
// This hook enables testing the case where execution of this method loses the race with
|
||||||
|
// apartment rundown by other means.
|
||||||
|
if constexpr (test_hook::AsyncRundownDelayForTestingRaces != INFINITE)
|
||||||
|
{
|
||||||
|
Sleep(test_hook::AsyncRundownDelayForTestingRaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto&& context : contexts)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
co_await context;
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
catch (winrt::hresult_error const& e)
|
||||||
|
{
|
||||||
|
// Ignore failure if apartment ran down before we could clean it up.
|
||||||
|
// The object already ran down as part of apartment cleanup.
|
||||||
|
if ((e.code() != RPC_E_SERVER_DIED_DNE) &&
|
||||||
|
(e.code() != RPC_E_DISCONNECTED))
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
FAIL_FAST();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const auto& storage()
|
||||||
|
{
|
||||||
|
return s_apartmentStorage.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t current_apartment_variable_count()
|
||||||
|
{
|
||||||
|
auto lock = winrt::slim_lock_guard(s_lock);
|
||||||
|
if (auto variable_storage = get_current_apartment_variable_storage())
|
||||||
|
{
|
||||||
|
return variable_storage->variables.size();
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apartment variables enable storing COM objects safely in globals
|
||||||
|
// (objects with static storage duration) by creating a unique copy
|
||||||
|
// in each apartment and managing their lifetime based on apartment rundown
|
||||||
|
// notifications.
|
||||||
|
// They can also be used for automatic or dynamic storage duration but those
|
||||||
|
// cases are less common.
|
||||||
|
// This type is also useful for storing references to apartment affine objects.
|
||||||
|
//
|
||||||
|
// Note, that apartment variables hosted in a COM DLL need to integrate with
|
||||||
|
// the DllCanUnloadNow() function to include the ref counts contributed by
|
||||||
|
// C++ WinRT objects. This is automatic for DLLs that host C++ WinRT objects
|
||||||
|
// but WRL projects will need to be updated to call winrt::get_module_lock().
|
||||||
|
|
||||||
|
template<typename T, apartment_variable_leak_action leak_action = apartment_variable_leak_action::fail_fast,
|
||||||
|
typename test_hook = wil::apartment_variable_platform>
|
||||||
|
struct apartment_variable : details::apartment_variable_base<leak_action, test_hook>
|
||||||
|
{
|
||||||
|
using base = details::apartment_variable_base<leak_action, test_hook>;
|
||||||
|
|
||||||
|
constexpr apartment_variable() = default;
|
||||||
|
|
||||||
|
// Get current value or throw if no value has been set.
|
||||||
|
T& get_existing() { return std::any_cast<T&>(base::get_existing()); }
|
||||||
|
|
||||||
|
// Get current value or default-construct one on demand.
|
||||||
|
T& get_or_create()
|
||||||
|
{
|
||||||
|
return std::any_cast<T&>(base::get_or_create(details::any_maker<T>()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current value or custom-construct one on demand.
|
||||||
|
template<typename F>
|
||||||
|
T& get_or_create(F&& f)
|
||||||
|
{
|
||||||
|
return std::any_cast<T&>(base::get_or_create(details::any_maker<T>(std::forward<F>(f))));
|
||||||
|
}
|
||||||
|
|
||||||
|
// get pointer to current value or nullptr if no value has been set
|
||||||
|
T* get_if() { return std::any_cast<T>(base::get_if()); }
|
||||||
|
|
||||||
|
// replace or create the current value, fail fasts if the value is not already stored
|
||||||
|
template<typename V> void set(V&& value) { return base::set(std::forward<V>(value)); }
|
||||||
|
|
||||||
|
// Clear the value in the current apartment.
|
||||||
|
using base::clear;
|
||||||
|
|
||||||
|
// Asynchronously clear the value in all apartments it is present in.
|
||||||
|
using base::clear_all_apartments_async;
|
||||||
|
|
||||||
|
// For testing only.
|
||||||
|
// 1) To observe the state of the storage in the debugger assign this to
|
||||||
|
// a temporary variable (const&) and watch its contents.
|
||||||
|
// 2) Use this to test the implementation.
|
||||||
|
using base::storage;
|
||||||
|
// For testing only. The number of variables in the current apartment.
|
||||||
|
using base::current_apartment_variable_count;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __WIL_COM_APARTMENT_VARIABLE_INCLUDED
|
|
@ -75,27 +75,7 @@
|
||||||
#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS
|
#define __WI_SUPPRESS_NOEXCEPT_ANALYSIS
|
||||||
#endif
|
#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 <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
|
// 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
|
// 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)
|
#define __declspec_selectany_ __declspec(selectany)
|
||||||
/// @endcond
|
/// @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
|
//! @defgroup macrobuilding Macro Composition
|
||||||
//! The following macros are building blocks primarily intended for authoring other macros.
|
//! The following macros are building blocks primarily intended for authoring other macros.
|
||||||
//! @{
|
//! @{
|
||||||
|
@ -327,8 +240,96 @@ Three exception modes are available:
|
||||||
|
|
||||||
//! @} // Macro composition helpers
|
//! @} // Macro composition helpers
|
||||||
|
|
||||||
#define __R_ENABLE_IF_IS_CLASS(ptrType) wistd::enable_if_t<wistd::is_class<ptrType>::value, void*> = (void*)0
|
#if !defined(__cplusplus) || defined(__WIL_MIN_KERNEL)
|
||||||
#define __R_ENABLE_IF_IS_NOT_CLASS(ptrType) wistd::enable_if_t<!wistd::is_class<ptrType>::value, void*> = (void*)0
|
|
||||||
|
#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
|
//! @defgroup bitwise Bitwise Inspection and Manipulation
|
||||||
//! Bitwise helpers to improve readability and reduce the error rate of bitwise operations.
|
//! Bitwise helpers to improve readability and reduce the error rate of bitwise operations.
|
||||||
|
@ -612,10 +613,10 @@ namespace wil
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
_Post_satisfies_(return == !!val)
|
_Post_satisfies_(return == (val != 0))
|
||||||
__forceinline constexpr bool verify_bool<unsigned char>(unsigned char val)
|
__forceinline constexpr bool verify_bool<unsigned char>(unsigned char val)
|
||||||
{
|
{
|
||||||
return !!val;
|
return (val != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Verify that `val` is a Win32 BOOL value.
|
/** 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));
|
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`. */
|
@return An HRESULT representing the evaluation of `val`. */
|
||||||
template <typename T>
|
template <typename T>
|
||||||
_Post_satisfies_(return == hr)
|
_Post_satisfies_(return == hr)
|
||||||
inline constexpr long verify_hresult(T 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");
|
static_assert(wistd::is_same<T, long>::value, "Wrong Type: HRESULT expected");
|
||||||
return hr;
|
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
|
/// @} // end type validation routines
|
||||||
|
|
||||||
/// @cond
|
/// @cond
|
||||||
|
@ -706,31 +753,31 @@ namespace wil
|
||||||
template <>
|
template <>
|
||||||
struct variable_size<1>
|
struct variable_size<1>
|
||||||
{
|
{
|
||||||
typedef unsigned char type;
|
using type = unsigned char;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct variable_size<2>
|
struct variable_size<2>
|
||||||
{
|
{
|
||||||
typedef unsigned short type;
|
using type = unsigned short;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct variable_size<4>
|
struct variable_size<4>
|
||||||
{
|
{
|
||||||
typedef unsigned long type;
|
using type = unsigned long;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct variable_size<8>
|
struct variable_size<8>
|
||||||
{
|
{
|
||||||
typedef unsigned long long type;
|
using type = unsigned long long;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct variable_size_mapping
|
struct variable_size_mapping
|
||||||
{
|
{
|
||||||
typedef typename variable_size<sizeof(T)>::type type;
|
using type = typename variable_size<sizeof(T)>::type;
|
||||||
};
|
};
|
||||||
} // details
|
} // details
|
||||||
/// @endcond
|
/// @endcond
|
||||||
|
@ -739,6 +786,10 @@ namespace wil
|
||||||
This allows code to generically convert any enum class to it's corresponding underlying type. */
|
This allows code to generically convert any enum class to it's corresponding underlying type. */
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using integral_from_enum = typename details::variable_size_mapping<T>::type;
|
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
|
} // wil
|
||||||
|
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <unknwn.h>
|
#include <unknwn.h>
|
||||||
|
#include <inspectable.h>
|
||||||
#include <hstring.h>
|
#include <hstring.h>
|
||||||
|
|
||||||
// WIL and C++/WinRT use two different exception types for communicating HRESULT failures. Thus, both libraries need to
|
// 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
|
namespace wil::details
|
||||||
{
|
{
|
||||||
// Since the C++/WinRT version macro is a string...
|
// 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;
|
int result = 0;
|
||||||
auto str = versionString;
|
while ((*versionString >= '0') && (*versionString <= '9'))
|
||||||
while ((*str >= '0') && (*str <= '9'))
|
|
||||||
{
|
{
|
||||||
result = result * 10 + (*str - '0');
|
result = result * 10 + (*versionString - '0');
|
||||||
++str;
|
++versionString;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
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
|
/// @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
|
// use it unless the version of C++/WinRT is high enough
|
||||||
extern std::int32_t(__stdcall* winrt_to_hresult_handler)(void*) noexcept;
|
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
|
/// @cond
|
||||||
namespace wil::details
|
namespace wil::details
|
||||||
{
|
{
|
||||||
|
@ -108,7 +137,7 @@ namespace wil::details
|
||||||
catch (const winrt::hresult_error& exception)
|
catch (const winrt::hresult_error& exception)
|
||||||
{
|
{
|
||||||
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||||
return exception.code().value;
|
return exception.to_abi();
|
||||||
}
|
}
|
||||||
catch (const std::bad_alloc& exception)
|
catch (const std::bad_alloc& exception)
|
||||||
{
|
{
|
||||||
|
@ -149,7 +178,7 @@ namespace wil::details
|
||||||
catch (const winrt::hresult_error& exception)
|
catch (const winrt::hresult_error& exception)
|
||||||
{
|
{
|
||||||
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
MaybeGetExceptionString(exception, debugString, debugStringChars);
|
||||||
return exception.code().value;
|
return exception.to_abi();
|
||||||
}
|
}
|
||||||
catch (const std::bad_alloc& exception)
|
catch (const std::bad_alloc& exception)
|
||||||
{
|
{
|
||||||
|
@ -192,6 +221,12 @@ namespace wil
|
||||||
return static_cast<std::int32_t>(details::ReportFailure_CaughtException<FailureType::Return>(__R_DIAGNOSTICS_RA(DiagnosticsInfo{}, returnAddress)));
|
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()
|
inline void WilInitialize_CppWinRT()
|
||||||
{
|
{
|
||||||
details::g_pfnResultFromCaughtException_CppWinRt = details::ResultFromCaughtException_CppWinRt;
|
details::g_pfnResultFromCaughtException_CppWinRt = details::ResultFromCaughtException_CppWinRt;
|
||||||
|
@ -199,6 +234,12 @@ namespace wil
|
||||||
{
|
{
|
||||||
WI_ASSERT(winrt_to_hresult_handler == nullptr);
|
WI_ASSERT(winrt_to_hresult_handler == nullptr);
|
||||||
winrt_to_hresult_handler = winrt_to_hresult;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,6 +277,11 @@ namespace wil
|
||||||
return static_cast<HSTRING>(winrt::get_abi(object));
|
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>
|
template <typename T>
|
||||||
auto put_abi(T& object) noexcept
|
auto put_abi(T& object) noexcept
|
||||||
{
|
{
|
||||||
|
@ -246,6 +292,117 @@ namespace wil
|
||||||
{
|
{
|
||||||
return reinterpret_cast<HSTRING*>(winrt::put_abi(object));
|
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
|
#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;
|
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()
|
//! 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?
|
//! 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)
|
inline PCWSTR find_last_path_segment(_In_ PCWSTR path)
|
||||||
|
@ -51,6 +52,7 @@ namespace wil
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//! Determine if the file name is one of the special "." or ".." names.
|
//! Determine if the file name is one of the special "." or ".." names.
|
||||||
inline bool path_is_dot_or_dotdot(_In_ PCWSTR fileName)
|
inline bool path_is_dot_or_dotdot(_In_ PCWSTR fileName)
|
||||||
|
@ -83,7 +85,7 @@ namespace wil
|
||||||
return false;
|
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.
|
// PathCch.h APIs are only in desktop API for now.
|
||||||
|
|
||||||
|
@ -111,7 +113,7 @@ namespace wil
|
||||||
{
|
{
|
||||||
if (::CreateDirectoryW(path, nullptr) == FALSE)
|
if (::CreateDirectoryW(path, nullptr) == FALSE)
|
||||||
{
|
{
|
||||||
DWORD const lastError = ::GetLastError();
|
DWORD lastError = ::GetLastError();
|
||||||
if (lastError == ERROR_PATH_NOT_FOUND)
|
if (lastError == ERROR_PATH_NOT_FOUND)
|
||||||
{
|
{
|
||||||
size_t parentLength;
|
size_t parentLength;
|
||||||
|
@ -120,9 +122,16 @@ namespace wil
|
||||||
wistd::unique_ptr<wchar_t[]> parent(new (std::nothrow) wchar_t[parentLength + 1]);
|
wistd::unique_ptr<wchar_t[]> parent(new (std::nothrow) wchar_t[parentLength + 1]);
|
||||||
RETURN_IF_NULL_ALLOC(parent.get());
|
RETURN_IF_NULL_ALLOC(parent.get());
|
||||||
RETURN_IF_FAILED(StringCchCopyNW(parent.get(), parentLength + 1, path, parentLength));
|
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)
|
else if (lastError != ERROR_ALREADY_EXISTS)
|
||||||
{
|
{
|
||||||
|
@ -183,13 +192,53 @@ namespace wil
|
||||||
enum class RemoveDirectoryOptions
|
enum class RemoveDirectoryOptions
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
KeepRootDirectory = 0x1
|
KeepRootDirectory = 0x1,
|
||||||
|
RemoveReadOnly = 0x2,
|
||||||
};
|
};
|
||||||
DEFINE_ENUM_FLAG_OPERATORS(RemoveDirectoryOptions);
|
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
|
// If inputPath is a non-normalized name be sure to pass an extended length form to ensure
|
||||||
// it can be addressed and deleted.
|
// 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;
|
wil::unique_hlocal_string path;
|
||||||
PATHCCH_OPTIONS combineOptions = PATHCCH_NONE;
|
PATHCCH_OPTIONS combineOptions = PATHCCH_NONE;
|
||||||
|
@ -227,17 +276,53 @@ namespace wil
|
||||||
RETURN_IF_FAILED(::PathAllocCombine(path.get(), fd.cFileName,
|
RETURN_IF_FAILED(::PathAllocCombine(path.get(), fd.cFileName,
|
||||||
PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH | PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, &pathToDelete));
|
PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH | PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, &pathToDelete));
|
||||||
if (WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY))
|
if (WI_IsFlagSet(fd.dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY))
|
||||||
|
{
|
||||||
|
// 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;
|
RemoveDirectoryOptions localOptions = options;
|
||||||
RETURN_IF_FAILED(RemoveDirectoryRecursiveNoThrow(pathToDelete.get(), WI_ClearFlag(localOptions, RemoveDirectoryOptions::KeepRootDirectory)));
|
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
|
else
|
||||||
{
|
{
|
||||||
// note: if pathToDelete is read-only this will fail, consider adding
|
// Failed to grab a handle to the file or to read its attributes. This is not safe to recurse.
|
||||||
// RemoveDirectoryOptions::RemoveReadOnly to enable this behavior.
|
RETURN_WIN32(::GetLastError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 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()));
|
RETURN_IF_WIN32_BOOL_FALSE(::DeleteFileW(pathToDelete.get()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!::FindNextFileW(findHandle.get(), &fd))
|
if (!::FindNextFileW(findHandle.get(), &fd))
|
||||||
{
|
{
|
||||||
|
@ -251,9 +336,37 @@ namespace wil
|
||||||
}
|
}
|
||||||
|
|
||||||
if (WI_IsFlagClear(options, RemoveDirectoryOptions::KeepRootDirectory))
|
if (WI_IsFlagClear(options, RemoveDirectoryOptions::KeepRootDirectory))
|
||||||
|
{
|
||||||
|
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_IF_WIN32_BOOL_FALSE(::RemoveDirectoryW(path.get()));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,7 +665,7 @@ namespace wil
|
||||||
OVERLAPPED m_overlapped{};
|
OVERLAPPED m_overlapped{};
|
||||||
TP_IO *m_tpIo = __nullptr;
|
TP_IO *m_tpIo = __nullptr;
|
||||||
srwlock m_cancelLock;
|
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; }
|
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);
|
auto readerState = static_cast<details::folder_change_reader_state *>(context);
|
||||||
// WI_ASSERT(overlapped == &readerState->m_overlapped);
|
// WI_ASSERT(overlapped == &readerState->m_overlapped);
|
||||||
|
|
||||||
bool requeue = true;
|
|
||||||
if (result == ERROR_SUCCESS)
|
if (result == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
for (auto const& info : create_next_entry_offset_iterator(reinterpret_cast<FILE_NOTIFY_INFORMATION *>(readerState->m_readBuffer)))
|
for (auto const& info : create_next_entry_offset_iterator(reinterpret_cast<FILE_NOTIFY_INFORMATION *>(readerState->m_readBuffer)))
|
||||||
|
@ -613,11 +725,10 @@ namespace wil
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
requeue = false;
|
// No need to requeue
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requeue)
|
|
||||||
{
|
|
||||||
// If the lock is held non-shared or the TP IO is nullptr, this
|
// If the lock is held non-shared or the TP IO is nullptr, this
|
||||||
// structure is being torn down. Otherwise, monitor for further
|
// structure is being torn down. Otherwise, monitor for further
|
||||||
// changes.
|
// changes.
|
||||||
|
@ -627,7 +738,6 @@ namespace wil
|
||||||
readerState->StartIo(); // ignoring failure here
|
readerState->StartIo(); // ignoring failure here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// This function exists to avoid template expansion of this code based on err_policy.
|
// 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)
|
HRESULT create_common(PCWSTR folderToWatch, bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
|
||||||
|
@ -798,7 +908,7 @@ namespace wil
|
||||||
|
|
||||||
// Type unsafe version used in the implementation to avoid template bloat.
|
// 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,
|
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;
|
*result = nullptr;
|
||||||
|
|
||||||
|
@ -875,6 +985,36 @@ namespace wil
|
||||||
return S_OK;
|
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
|
#ifdef _CPPUNWIND
|
||||||
/** Get file information for a fixed sized structure, throws on failure.
|
/** Get file information for a fixed sized structure, throws on failure.
|
||||||
~~~
|
~~~
|
||||||
|
@ -902,7 +1042,7 @@ namespace wil
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
#endif // _CPPUNWIND
|
#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
|
#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
|
File diff suppressed because it is too large
Load Diff
|
@ -252,7 +252,8 @@ namespace wil
|
||||||
if (ProcessShutdownInProgress())
|
if (ProcessShutdownInProgress())
|
||||||
{
|
{
|
||||||
// There are no other threads to contend with.
|
// There are no other threads to contend with.
|
||||||
if (--m_refCount == 0)
|
m_refCount = m_refCount - 1;
|
||||||
|
if (m_refCount == 0)
|
||||||
{
|
{
|
||||||
m_data.ProcessShutdown();
|
m_data.ProcessShutdown();
|
||||||
}
|
}
|
||||||
|
@ -260,7 +261,8 @@ namespace wil
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto lock = m_mutex.acquire();
|
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
|
// We must explicitly destroy our semaphores while holding the mutex
|
||||||
m_value.Destroy();
|
m_value.Destroy();
|
||||||
|
@ -281,7 +283,7 @@ namespace wil
|
||||||
|
|
||||||
const DWORD size = static_cast<DWORD>(sizeof(ProcessLocalStorageData<T>));
|
const DWORD size = static_cast<DWORD>(sizeof(ProcessLocalStorageData<T>));
|
||||||
wchar_t name[MAX_PATH];
|
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;
|
unique_mutex_nothrow mutex;
|
||||||
mutex.reset(::CreateMutexExW(nullptr, name, 0, MUTEX_ALL_ACCESS));
|
mutex.reset(::CreateMutexExW(nullptr, name, 0, MUTEX_ALL_ACCESS));
|
||||||
|
@ -295,7 +297,7 @@ namespace wil
|
||||||
if (pointer)
|
if (pointer)
|
||||||
{
|
{
|
||||||
*data = reinterpret_cast<ProcessLocalStorageData<T>*>(pointer);
|
*data = reinterpret_cast<ProcessLocalStorageData<T>*>(pointer);
|
||||||
(*data)->m_refCount++;
|
(*data)->m_refCount = (*data)->m_refCount + 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -312,13 +314,13 @@ namespace wil
|
||||||
SemaphoreValue m_value;
|
SemaphoreValue m_value;
|
||||||
T m_data;
|
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;
|
*data = nullptr;
|
||||||
|
|
||||||
const DWORD size = static_cast<DWORD>(sizeof(ProcessLocalStorageData<T>));
|
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);
|
__WIL_PRIVATE_RETURN_IF_NULL_ALLOC(dataAlloc);
|
||||||
|
|
||||||
SemaphoreValue semaphoreValue;
|
SemaphoreValue semaphoreValue;
|
||||||
|
@ -406,10 +408,9 @@ namespace wil
|
||||||
|
|
||||||
if (shouldAllocate)
|
if (shouldAllocate)
|
||||||
{
|
{
|
||||||
Node *pNew = reinterpret_cast<Node *>(::HeapAlloc(::GetProcessHeap(), 0, sizeof(Node)));
|
if (auto pNewRaw = details::ProcessHeapAlloc(0, sizeof(Node)))
|
||||||
if (pNew != nullptr)
|
|
||||||
{
|
{
|
||||||
new(pNew)Node{ threadId };
|
auto pNew = new (pNewRaw) Node{ threadId };
|
||||||
|
|
||||||
Node *pFirst;
|
Node *pFirst;
|
||||||
do
|
do
|
||||||
|
@ -428,7 +429,7 @@ namespace wil
|
||||||
|
|
||||||
struct Node
|
struct Node
|
||||||
{
|
{
|
||||||
DWORD threadId;
|
DWORD threadId = ULONG_MAX;
|
||||||
Node* pNext = nullptr;
|
Node* pNext = nullptr;
|
||||||
T value{};
|
T value{};
|
||||||
};
|
};
|
||||||
|
@ -487,7 +488,7 @@ namespace wil
|
||||||
|
|
||||||
if (!stringBuffer || (stringBufferSize < neededSize))
|
if (!stringBuffer || (stringBufferSize < neededSize))
|
||||||
{
|
{
|
||||||
auto newBuffer = ::HeapAlloc(::GetProcessHeap(), HEAP_ZERO_MEMORY, neededSize);
|
auto newBuffer = details::ProcessHeapAlloc(HEAP_ZERO_MEMORY, neededSize);
|
||||||
if (newBuffer)
|
if (newBuffer)
|
||||||
{
|
{
|
||||||
::HeapFree(::GetProcessHeap(), 0, stringBuffer);
|
::HeapFree(::GetProcessHeap(), 0, stringBuffer);
|
||||||
|
@ -508,7 +509,7 @@ namespace wil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Get(FailureInfo& info)
|
void Get(FailureInfo& info) const
|
||||||
{
|
{
|
||||||
::ZeroMemory(&info, sizeof(info));
|
::ZeroMemory(&info, sizeof(info));
|
||||||
|
|
||||||
|
@ -565,7 +566,7 @@ namespace wil
|
||||||
if (!errors && create)
|
if (!errors && create)
|
||||||
{
|
{
|
||||||
const unsigned short errorCount = 5;
|
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)
|
if (errors)
|
||||||
{
|
{
|
||||||
errorAllocCount = errorCount;
|
errorAllocCount = errorCount;
|
||||||
|
@ -611,7 +612,7 @@ namespace wil
|
||||||
errors[errorCurrentIndex].Set(info, ::InterlockedIncrementNoFence(failureSequenceId));
|
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)
|
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.
|
// 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];
|
wchar_t message[2048];
|
||||||
message[0] = L'\0';
|
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.
|
// Now that the exception was logged, we should be able to fetch it.
|
||||||
return GetLastError(info, minSequenceId, hr);
|
return GetLastError(info, minSequenceId, hr);
|
||||||
|
@ -958,7 +959,7 @@ namespace wil
|
||||||
m_ppThreadList = nullptr;
|
m_ppThreadList = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsWatching()
|
bool IsWatching() const
|
||||||
{
|
{
|
||||||
return (m_threadId != 0);
|
return (m_threadId != 0);
|
||||||
}
|
}
|
||||||
|
@ -1044,7 +1045,9 @@ namespace wil
|
||||||
|
|
||||||
if (g_pfnTelemetryCallback != nullptr)
|
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);
|
return m_errorFunction(failure);
|
||||||
}
|
}
|
||||||
|
@ -1251,7 +1254,7 @@ namespace wil
|
||||||
return (FAILED(m_failure.GetFailureInfo().hr) ? &(m_failure.GetFailureInfo()) : nullptr);
|
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
|
// 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).
|
// generated, so we ignore subsequent failures on the same error code (assuming propagation).
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -31,7 +31,6 @@
|
||||||
#include "com.h"
|
#include "com.h"
|
||||||
#include <roerrorapi.h>
|
#include <roerrorapi.h>
|
||||||
|
|
||||||
#ifndef __cplusplus_winrt // The CX runtime likes to originate errors already so we would conflict with them.
|
|
||||||
namespace wil
|
namespace wil
|
||||||
{
|
{
|
||||||
namespace details
|
namespace details
|
||||||
|
@ -82,6 +81,36 @@ 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 details
|
||||||
} // namespace wil
|
} // namespace wil
|
||||||
|
|
||||||
|
@ -89,8 +118,8 @@ namespace wil
|
||||||
WI_HEADER_INITITALIZATION_FUNCTION(ResultStowedExceptionInitialize, []
|
WI_HEADER_INITITALIZATION_FUNCTION(ResultStowedExceptionInitialize, []
|
||||||
{
|
{
|
||||||
::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions);
|
::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions);
|
||||||
|
::wil::SetFailfastWithContextCallback(::wil::details::FailfastWithContextCallback);
|
||||||
return 1;
|
return 1;
|
||||||
});
|
})
|
||||||
#endif // __cplusplus_winrt
|
|
||||||
|
|
||||||
#endif // __WIL_RESULT_ORIGINATE_INCLUDED
|
#endif // __WIL_RESULT_ORIGINATE_INCLUDED
|
||||||
|
|
|
@ -121,76 +121,76 @@ namespace wil
|
||||||
// Mappings of all conversions defined in intsafe.h to intsafe_conversion
|
// Mappings of all conversions defined in intsafe.h to intsafe_conversion
|
||||||
// Note: Uppercase types (UINT, DWORD, SIZE_T, etc) and architecture dependent types resolve
|
// 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.
|
// 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<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, char> = LongLongToChar;
|
||||||
template<> constexpr auto intsafe_conversion<__int64, int> = LongLongToInt;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, int> = LongLongToInt;
|
||||||
template<> constexpr auto intsafe_conversion<__int64, long> = LongLongToLong;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, long> = LongLongToLong;
|
||||||
template<> constexpr auto intsafe_conversion<__int64, short> = LongLongToShort;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, short> = LongLongToShort;
|
||||||
template<> constexpr auto intsafe_conversion<__int64, signed char> = LongLongToInt8;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, signed char> = LongLongToInt8;
|
||||||
template<> constexpr auto intsafe_conversion<__int64, unsigned __int64> = LongLongToULongLong;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned __int64> = LongLongToULongLong;
|
||||||
template<> constexpr auto intsafe_conversion<__int64, unsigned char> = LongLongToUChar;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned char> = LongLongToUChar;
|
||||||
template<> constexpr auto intsafe_conversion<__int64, unsigned int> = LongLongToUInt;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned int> = LongLongToUInt;
|
||||||
template<> constexpr auto intsafe_conversion<__int64, unsigned long> = LongLongToULong;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned long> = LongLongToULong;
|
||||||
template<> constexpr auto intsafe_conversion<__int64, unsigned short> = LongLongToUShort;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<__int64, unsigned short> = LongLongToUShort;
|
||||||
template<> constexpr auto intsafe_conversion<int, char> = IntToChar;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, char> = IntToChar;
|
||||||
template<> constexpr auto intsafe_conversion<int, short> = IntToShort;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, short> = IntToShort;
|
||||||
template<> constexpr auto intsafe_conversion<int, signed char> = IntToInt8;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, signed char> = IntToInt8;
|
||||||
template<> constexpr auto intsafe_conversion<int, unsigned __int64> = IntToULongLong;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned __int64> = IntToULongLong;
|
||||||
template<> constexpr auto intsafe_conversion<int, unsigned char> = IntToUChar;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned char> = IntToUChar;
|
||||||
template<> constexpr auto intsafe_conversion<int, unsigned int> = IntToUInt;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned int> = IntToUInt;
|
||||||
template<> constexpr auto intsafe_conversion<int, unsigned long> = IntToULong;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned long> = IntToULong;
|
||||||
template<> constexpr auto intsafe_conversion<int, unsigned short> = IntToUShort;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<int, unsigned short> = IntToUShort;
|
||||||
template<> constexpr auto intsafe_conversion<long, char> = LongToChar;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, char> = LongToChar;
|
||||||
template<> constexpr auto intsafe_conversion<long, int> = LongToInt;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, int> = LongToInt;
|
||||||
template<> constexpr auto intsafe_conversion<long, short> = LongToShort;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, short> = LongToShort;
|
||||||
template<> constexpr auto intsafe_conversion<long, signed char> = LongToInt8;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, signed char> = LongToInt8;
|
||||||
template<> constexpr auto intsafe_conversion<long, unsigned __int64> = LongToULongLong;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned __int64> = LongToULongLong;
|
||||||
template<> constexpr auto intsafe_conversion<long, unsigned char> = LongToUChar;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned char> = LongToUChar;
|
||||||
template<> constexpr auto intsafe_conversion<long, unsigned int> = LongToUInt;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned int> = LongToUInt;
|
||||||
template<> constexpr auto intsafe_conversion<long, unsigned long> = LongToULong;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned long> = LongToULong;
|
||||||
template<> constexpr auto intsafe_conversion<long, unsigned short> = LongToUShort;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<long, unsigned short> = LongToUShort;
|
||||||
template<> constexpr auto intsafe_conversion<short, char> = ShortToChar;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, char> = ShortToChar;
|
||||||
template<> constexpr auto intsafe_conversion<short, signed char> = ShortToInt8;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, signed char> = ShortToInt8;
|
||||||
template<> constexpr auto intsafe_conversion<short, unsigned __int64> = ShortToULongLong;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned __int64> = ShortToULongLong;
|
||||||
template<> constexpr auto intsafe_conversion<short, unsigned char> = ShortToUChar;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned char> = ShortToUChar;
|
||||||
template<> constexpr auto intsafe_conversion<short, unsigned int> = ShortToUInt;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned int> = ShortToUInt;
|
||||||
template<> constexpr auto intsafe_conversion<short, unsigned long> = ShortToULong;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned long> = ShortToULong;
|
||||||
template<> constexpr auto intsafe_conversion<short, unsigned short> = ShortToUShort;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<short, unsigned short> = ShortToUShort;
|
||||||
template<> constexpr auto intsafe_conversion<signed char, unsigned __int64> = Int8ToULongLong;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned __int64> = Int8ToULongLong;
|
||||||
template<> constexpr auto intsafe_conversion<signed char, unsigned char> = Int8ToUChar;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned char> = Int8ToUChar;
|
||||||
template<> constexpr auto intsafe_conversion<signed char, unsigned int> = Int8ToUInt;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned int> = Int8ToUInt;
|
||||||
template<> constexpr auto intsafe_conversion<signed char, unsigned long> = Int8ToULong;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned long> = Int8ToULong;
|
||||||
template<> constexpr auto intsafe_conversion<signed char, unsigned short> = Int8ToUShort;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<signed char, unsigned short> = Int8ToUShort;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned __int64, __int64> = ULongLongToLongLong;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, __int64> = ULongLongToLongLong;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned __int64, char> = ULongLongToChar;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, char> = ULongLongToChar;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned __int64, int> = ULongLongToInt;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, int> = ULongLongToInt;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned __int64, long> = ULongLongToLong;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, long> = ULongLongToLong;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned __int64, short> = ULongLongToShort;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, short> = ULongLongToShort;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned __int64, signed char> = ULongLongToInt8;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, signed char> = ULongLongToInt8;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned char> = ULongLongToUChar;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned char> = ULongLongToUChar;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned int> = ULongLongToUInt;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned int> = ULongLongToUInt;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned long> = ULongLongToULong;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned long> = ULongLongToULong;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned __int64, unsigned short> = ULongLongToUShort;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned __int64, unsigned short> = ULongLongToUShort;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned char, char> = UInt8ToChar;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned char, char> = UInt8ToChar;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned char, signed char> = UIntToInt8;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned char, signed char> = UIntToInt8;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned int, char> = UIntToChar;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, char> = UIntToChar;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned int, int> = UIntToInt;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, int> = UIntToInt;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned int, long> = UIntToLong;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, long> = UIntToLong;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned int, short> = UIntToShort;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, short> = UIntToShort;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned int, signed char> = UIntToInt8;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, signed char> = UIntToInt8;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned int, unsigned char> = UIntToUChar;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, unsigned char> = UIntToUChar;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned int, unsigned short> = UIntToUShort;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned int, unsigned short> = UIntToUShort;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned long, char> = ULongToChar;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, char> = ULongToChar;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned long, int> = ULongToInt;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, int> = ULongToInt;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned long, long> = ULongToLong;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, long> = ULongToLong;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned long, short> = ULongToShort;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, short> = ULongToShort;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned long, signed char> = ULongToInt8;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, signed char> = ULongToInt8;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned long, unsigned char> = ULongToUChar;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, unsigned char> = ULongToUChar;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned long, unsigned int> = ULongToUInt;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, unsigned int> = ULongToUInt;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned long, unsigned short> = ULongToUShort;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned long, unsigned short> = ULongToUShort;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned short, char> = UShortToChar;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, char> = UShortToChar;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned short, short> = UShortToShort;
|
template<> __WI_LIBCPP_INLINE_VAR constexpr auto intsafe_conversion<unsigned short, short> = UShortToShort;
|
||||||
template<> constexpr auto intsafe_conversion<unsigned short, signed char> = UShortToInt8;
|
template<> __WI_LIBCPP_INLINE_VAR 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<unsigned short, unsigned char> = UShortToUChar;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unsafe conversion where failure results in fail fast.
|
// Unsafe conversion where failure results in fail fast.
|
||||||
|
|
|
@ -15,19 +15,17 @@
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <utility>
|
||||||
|
#if _HAS_CXX17
|
||||||
|
#include <string_view>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WI_STL_FAIL_FAST_IF
|
||||||
|
#define WI_STL_FAIL_FAST_IF FAIL_FAST_IF
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(WIL_ENABLE_EXCEPTIONS)
|
#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
|
namespace wil
|
||||||
{
|
{
|
||||||
|
@ -42,7 +40,7 @@ namespace wil
|
||||||
template<typename Other>
|
template<typename Other>
|
||||||
struct rebind
|
struct rebind
|
||||||
{
|
{
|
||||||
typedef secure_allocator<Other> other;
|
using other = secure_allocator<Other>;
|
||||||
};
|
};
|
||||||
|
|
||||||
secure_allocator()
|
secure_allocator()
|
||||||
|
@ -100,6 +98,8 @@ namespace wil
|
||||||
|
|
||||||
wchar_t* buffer() { return &m_value[0]; }
|
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)); }
|
std::wstring release() { return std::wstring(std::move(m_value)); }
|
||||||
|
|
||||||
static PCWSTR get(const std::wstring& value) { return value.c_str(); }
|
static PCWSTR get(const std::wstring& value) { return value.c_str(); }
|
||||||
|
@ -117,6 +117,78 @@ namespace wil
|
||||||
return str.c_str();
|
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
|
} // namespace wil
|
||||||
|
|
||||||
#endif // WIL_ENABLE_EXCEPTIONS
|
#endif // WIL_ENABLE_EXCEPTIONS
|
||||||
|
|
|
@ -21,7 +21,9 @@
|
||||||
#include <processthreadsapi.h>
|
#include <processthreadsapi.h>
|
||||||
|
|
||||||
// for GetUserNameEx()
|
// for GetUserNameEx()
|
||||||
|
#ifndef SECURITY_WIN32
|
||||||
#define SECURITY_WIN32
|
#define SECURITY_WIN32
|
||||||
|
#endif
|
||||||
#include <Security.h>
|
#include <Security.h>
|
||||||
|
|
||||||
namespace wil
|
namespace wil
|
||||||
|
@ -34,25 +36,25 @@ namespace wil
|
||||||
// be an info class value that uses the same structure. That is the case for the file
|
// be an info class value that uses the same structure. That is the case for the file
|
||||||
// system information.
|
// system information.
|
||||||
template<typename T> struct MapTokenStructToInfoClass;
|
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_ACCESS_INFORMATION> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenAccessInformation; static constexpr 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_APPCONTAINER_INFORMATION> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenAppContainerSid; static constexpr 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_DEFAULT_DACL> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenDefaultDacl; static constexpr 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_GROUPS_AND_PRIVILEGES> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenGroupsAndPrivileges; static constexpr 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_MANDATORY_LABEL> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenIntegrityLevel; static constexpr bool FixedSize = false; };
|
||||||
template<> struct MapTokenStructToInfoClass<TOKEN_OWNER> { static const TOKEN_INFORMATION_CLASS infoClass = TokenOwner; static const bool FixedSize = false; };
|
template<> struct MapTokenStructToInfoClass<TOKEN_OWNER> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenOwner; static constexpr 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_PRIMARY_GROUP> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenPrimaryGroup; static constexpr bool FixedSize = false; };
|
||||||
template<> struct MapTokenStructToInfoClass<TOKEN_PRIVILEGES> { static const TOKEN_INFORMATION_CLASS infoClass = TokenPrivileges; static const bool FixedSize = false; };
|
template<> struct MapTokenStructToInfoClass<TOKEN_PRIVILEGES> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenPrivileges; static constexpr bool FixedSize = false; };
|
||||||
template<> struct MapTokenStructToInfoClass<TOKEN_USER> { static const TOKEN_INFORMATION_CLASS infoClass = TokenUser; static const bool FixedSize = false; };
|
template<> struct MapTokenStructToInfoClass<TOKEN_USER> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenUser; static constexpr bool FixedSize = false; };
|
||||||
|
|
||||||
// fixed size cases
|
// 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_ELEVATION_TYPE> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenElevationType; static constexpr 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_MANDATORY_POLICY> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenMandatoryPolicy; static constexpr bool FixedSize = true; };
|
||||||
template<> struct MapTokenStructToInfoClass<TOKEN_ORIGIN> { static const TOKEN_INFORMATION_CLASS infoClass = TokenOrigin; static const bool FixedSize = true; };
|
template<> struct MapTokenStructToInfoClass<TOKEN_ORIGIN> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenOrigin; static constexpr bool FixedSize = true; };
|
||||||
template<> struct MapTokenStructToInfoClass<TOKEN_SOURCE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenSource; static const bool FixedSize = true; };
|
template<> struct MapTokenStructToInfoClass<TOKEN_SOURCE> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenSource; static constexpr bool FixedSize = true; };
|
||||||
template<> struct MapTokenStructToInfoClass<TOKEN_STATISTICS> { static const TOKEN_INFORMATION_CLASS infoClass = TokenStatistics; static const bool FixedSize = true; };
|
template<> struct MapTokenStructToInfoClass<TOKEN_STATISTICS> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenStatistics; static constexpr bool FixedSize = true; };
|
||||||
template<> struct MapTokenStructToInfoClass<TOKEN_TYPE> { static const TOKEN_INFORMATION_CLASS infoClass = TokenType; static const bool FixedSize = true; };
|
template<> struct MapTokenStructToInfoClass<TOKEN_TYPE> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenType; static constexpr bool FixedSize = true; };
|
||||||
template<> struct MapTokenStructToInfoClass<SECURITY_IMPERSONATION_LEVEL> { static const TOKEN_INFORMATION_CLASS infoClass = TokenImpersonationLevel; static const bool FixedSize = true; };
|
template<> struct MapTokenStructToInfoClass<SECURITY_IMPERSONATION_LEVEL> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenImpersonationLevel; static constexpr bool FixedSize = true; };
|
||||||
template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION> { static const TOKEN_INFORMATION_CLASS infoClass = TokenElevation; static const bool FixedSize = true; };
|
template<> struct MapTokenStructToInfoClass<TOKEN_ELEVATION> { static constexpr TOKEN_INFORMATION_CLASS infoClass = TokenElevation; static constexpr bool FixedSize = true; };
|
||||||
}
|
}
|
||||||
/// @endcond
|
/// @endcond
|
||||||
|
|
||||||
|
@ -85,13 +87,12 @@ namespace wil
|
||||||
or privilege-adjustment are examples of uses.
|
or privilege-adjustment are examples of uses.
|
||||||
~~~~
|
~~~~
|
||||||
wil::unique_handle callerToken;
|
wil::unique_handle callerToken;
|
||||||
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_IMPERSONATE, true));
|
RETURN_IF_FAILED(wil::open_current_access_token_nothrow(&theToken, TOKEN_QUERY | TOKEN_IMPERSONATE, OpenThreadTokenAs::Self));
|
||||||
~~~~
|
~~~~
|
||||||
@param tokenHandle Receives the token opened during the operation. Must be CloseHandle'd by the caller, or
|
@param tokenHandle Receives the token opened during the operation. Must be CloseHandle'd by the caller, or
|
||||||
(preferably) stored in a wil::unique_handle
|
(preferably) stored in a wil::unique_handle
|
||||||
@param access Bits from the TOKEN_* access mask which are passed to OpenThreadToken/OpenProcessToken
|
@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
|
@param openAs Current to use current thread security context, or Self to use process security context.
|
||||||
process token's rights.
|
|
||||||
*/
|
*/
|
||||||
inline HRESULT open_current_access_token_nothrow(_Out_ HANDLE* tokenHandle, unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
|
inline HRESULT open_current_access_token_nothrow(_Out_ HANDLE* tokenHandle, unsigned long access = TOKEN_QUERY, OpenThreadTokenAs openAs = OpenThreadTokenAs::Current)
|
||||||
{
|
{
|
||||||
|
@ -122,6 +123,7 @@ namespace wil
|
||||||
}
|
}
|
||||||
#endif // WIL_ENABLE_EXCEPTIONS
|
#endif // WIL_ENABLE_EXCEPTIONS
|
||||||
|
|
||||||
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||||
// Returns tokenHandle or the effective thread token if tokenHandle is null.
|
// Returns tokenHandle or the effective thread token if tokenHandle is null.
|
||||||
// Note, this returns an token handle who's lifetime is managed independently
|
// Note, this returns an token handle who's lifetime is managed independently
|
||||||
// and it may be a pseudo token, don't free it!
|
// and it may be a pseudo token, don't free it!
|
||||||
|
@ -287,11 +289,12 @@ namespace wil
|
||||||
return tokenInfo;
|
return tokenInfo;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif // _WIN32_WINNT >= _WIN32_WINNT_WIN8
|
||||||
|
|
||||||
/// @cond
|
/// @cond
|
||||||
namespace details
|
namespace details
|
||||||
{
|
{
|
||||||
inline void RevertImpersonateToken(_Pre_opt_valid_ _Frees_ptr_opt_ HANDLE oldToken)
|
inline void RevertImpersonateToken(_In_ _Post_ptr_invalid_ HANDLE oldToken)
|
||||||
{
|
{
|
||||||
FAIL_FAST_IMMEDIATE_IF(!::SetThreadToken(nullptr, oldToken));
|
FAIL_FAST_IMMEDIATE_IF(!::SetThreadToken(nullptr, oldToken));
|
||||||
|
|
||||||
|
@ -524,6 +527,7 @@ namespace wil
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||||
/** Determine whether a token represents an app container
|
/** Determine whether a token represents an app container
|
||||||
This method uses the passed in token and emits a boolean indicating that
|
This method uses the passed in token and emits a boolean indicating that
|
||||||
whether TokenIsAppContainer is true.
|
whether TokenIsAppContainer is true.
|
||||||
|
@ -573,6 +577,7 @@ namespace wil
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
#endif // WIL_ENABLE_EXCEPTIONS
|
#endif // WIL_ENABLE_EXCEPTIONS
|
||||||
|
#endif // _WIN32_WINNT >= _WIN32_WINNT_WIN8
|
||||||
|
|
||||||
template<typename... Ts> bool test_token_membership_failfast(_In_opt_ HANDLE token,
|
template<typename... Ts> bool test_token_membership_failfast(_In_opt_ HANDLE token,
|
||||||
const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities)
|
const SID_IDENTIFIER_AUTHORITY& sidAuthority, Ts&&... subAuthorities)
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
#pragma once
|
||||||
|
//*********************************************************
|
||||||
|
//
|
||||||
|
// 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_TRACELOGGING_CONFIG_H
|
||||||
|
#define __WIL_TRACELOGGING_CONFIG_H
|
||||||
|
|
||||||
|
// Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition
|
||||||
|
// in this file configures the provider as a normal (non-telemetry) provider.
|
||||||
|
#define TraceLoggingOptionMicrosoftTelemetry() \
|
||||||
|
// Empty definition for TraceLoggingOptionMicrosoftTelemetry
|
||||||
|
|
||||||
|
// Configuration macro for use in TRACELOGGING_DEFINE_PROVIDER. The definition
|
||||||
|
// in this file configures the provider as a normal (non-telemetry) provider.
|
||||||
|
#define TraceLoggingOptionWindowsCoreTelemetry() \
|
||||||
|
// Empty definition for TraceLoggingOptionWindowsCoreTelemetry
|
||||||
|
|
||||||
|
// Event privacy tags. Use the PDT macro values for the tag parameter, e.g.:
|
||||||
|
// TraceLoggingWrite(...,
|
||||||
|
// TelemetryPrivacyDataTag(PDT_BrowsingHistory | PDT_ProductAndServiceUsage),
|
||||||
|
// ...);
|
||||||
|
#define TelemetryPrivacyDataTag(tag) TraceLoggingUInt64((tag), "PartA_PrivTags")
|
||||||
|
#define PDT_BrowsingHistory 0x0000000000000002u
|
||||||
|
#define PDT_DeviceConnectivityAndConfiguration 0x0000000000000800u
|
||||||
|
#define PDT_InkingTypingAndSpeechUtterance 0x0000000000020000u
|
||||||
|
#define PDT_ProductAndServicePerformance 0x0000000001000000u
|
||||||
|
#define PDT_ProductAndServiceUsage 0x0000000002000000u
|
||||||
|
#define PDT_SoftwareSetupAndInventory 0x0000000080000000u
|
||||||
|
|
||||||
|
// Event categories specified via keywords, e.g.:
|
||||||
|
// TraceLoggingWrite(...,
|
||||||
|
// TraceLoggingKeyword(MICROSOFT_KEYWORD_MEASURES),
|
||||||
|
// ...);
|
||||||
|
#define MICROSOFT_KEYWORD_CRITICAL_DATA 0x0000800000000000 // Bit 47
|
||||||
|
#define MICROSOFT_KEYWORD_MEASURES 0x0000400000000000 // Bit 46
|
||||||
|
#define MICROSOFT_KEYWORD_TELEMETRY 0x0000200000000000 // Bit 45
|
||||||
|
#define MICROSOFT_KEYWORD_RESERVED_44 0x0000100000000000 // Bit 44 (reserved for future assignment)
|
||||||
|
|
||||||
|
// Event categories specified via event tags, e.g.:
|
||||||
|
// TraceLoggingWrite(...,
|
||||||
|
// TraceLoggingEventTag(MICROSOFT_EVENTTAG_REALTIME_LATENCY),
|
||||||
|
// ...);
|
||||||
|
#define MICROSOFT_EVENTTAG_DROP_USER_IDS 0x00008000
|
||||||
|
#define MICROSOFT_EVENTTAG_AGGREGATE 0x00010000
|
||||||
|
#define MICROSOFT_EVENTTAG_DROP_PII_EXCEPT_IP 0x00020000
|
||||||
|
#define MICROSOFT_EVENTTAG_COSTDEFERRED_LATENCY 0x00040000
|
||||||
|
#define MICROSOFT_EVENTTAG_CORE_DATA 0x00080000
|
||||||
|
#define MICROSOFT_EVENTTAG_INJECT_XTOKEN 0x00100000
|
||||||
|
#define MICROSOFT_EVENTTAG_REALTIME_LATENCY 0x00200000
|
||||||
|
#define MICROSOFT_EVENTTAG_NORMAL_LATENCY 0x00400000
|
||||||
|
#define MICROSOFT_EVENTTAG_CRITICAL_PERSISTENCE 0x00800000
|
||||||
|
#define MICROSOFT_EVENTTAG_NORMAL_PERSISTENCE 0x01000000
|
||||||
|
#define MICROSOFT_EVENTTAG_DROP_PII 0x02000000
|
||||||
|
#define MICROSOFT_EVENTTAG_HASH_PII 0x04000000
|
||||||
|
#define MICROSOFT_EVENTTAG_MARK_PII 0x08000000
|
||||||
|
|
||||||
|
// Field categories specified via field tags, e.g.:
|
||||||
|
// TraceLoggingWrite(...,
|
||||||
|
// TraceLoggingString(szUser, "UserName", "User's name", MICROSOFT_FIELDTAG_HASH_PII),
|
||||||
|
// ...);
|
||||||
|
#define MICROSOFT_FIELDTAG_DROP_PII 0x04000000
|
||||||
|
#define MICROSOFT_FIELDTAG_HASH_PII 0x08000000
|
||||||
|
#endif // __WIL_TRACELOGGING_CONFIG_H
|
|
@ -15,33 +15,177 @@
|
||||||
#include <sysinfoapi.h> // GetSystemTimeAsFileTime
|
#include <sysinfoapi.h> // GetSystemTimeAsFileTime
|
||||||
#include <libloaderapi.h> // GetProcAddress
|
#include <libloaderapi.h> // GetProcAddress
|
||||||
#include <Psapi.h> // GetModuleFileNameExW (macro), K32GetModuleFileNameExW
|
#include <Psapi.h> // GetModuleFileNameExW (macro), K32GetModuleFileNameExW
|
||||||
#include <PathCch.h>
|
#include <winreg.h>
|
||||||
#include <objbase.h>
|
#include <objbase.h>
|
||||||
|
|
||||||
|
// detect std::bit_cast
|
||||||
|
#ifdef __has_include
|
||||||
|
# if (__cplusplus >= 202002L || _MSVC_LANG >= 202002L) && __has_include(<bit>)
|
||||||
|
# include <bit>
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __cpp_lib_bit_cast >= 201806L
|
||||||
|
# define __WI_CONSTEXPR_BIT_CAST constexpr
|
||||||
|
#else
|
||||||
|
# define __WI_CONSTEXPR_BIT_CAST inline
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "result.h"
|
#include "result.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
#include "wistd_functional.h"
|
#include "wistd_functional.h"
|
||||||
#include "wistd_type_traits.h"
|
#include "wistd_type_traits.h"
|
||||||
|
|
||||||
|
#if _HAS_CXX20 && defined(_STRING_VIEW_) && defined(_COMPARE_)
|
||||||
|
// If we're using c++20, then <compare> must be included to use the string ordinal functions
|
||||||
|
# define __WI_DEFINE_STRING_ORDINAL_FUNCTIONS
|
||||||
|
#elif !_HAS_CXX20 && defined(_STRING_VIEW_)
|
||||||
|
# define __WI_DEFINE_STRING_ORDINAL_FUNCTIONS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace wistd
|
||||||
|
{
|
||||||
|
#if defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
|
||||||
|
|
||||||
|
#if _HAS_CXX20
|
||||||
|
|
||||||
|
using weak_ordering = std::weak_ordering;
|
||||||
|
|
||||||
|
#else // _HAS_CXX20
|
||||||
|
|
||||||
|
struct weak_ordering
|
||||||
|
{
|
||||||
|
static const weak_ordering less;
|
||||||
|
static const weak_ordering equivalent;
|
||||||
|
static const weak_ordering greater;
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr bool operator==(const weak_ordering left, std::nullptr_t) noexcept
|
||||||
|
{
|
||||||
|
return left.m_value == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr bool operator!=(const weak_ordering left, std::nullptr_t) noexcept
|
||||||
|
{
|
||||||
|
return left.m_value != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr bool operator<(const weak_ordering left, std::nullptr_t) noexcept
|
||||||
|
{
|
||||||
|
return left.m_value < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr bool operator>(const weak_ordering left, std::nullptr_t) noexcept
|
||||||
|
{
|
||||||
|
return left.m_value > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr bool operator<=(const weak_ordering left, std::nullptr_t) noexcept
|
||||||
|
{
|
||||||
|
return left.m_value <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr bool operator>=(const weak_ordering left, std::nullptr_t) noexcept
|
||||||
|
{
|
||||||
|
return left.m_value >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr bool operator==(std::nullptr_t, const weak_ordering right) noexcept
|
||||||
|
{
|
||||||
|
return right == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr bool operator!=(std::nullptr_t, const weak_ordering right) noexcept
|
||||||
|
{
|
||||||
|
return right != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr bool operator<(std::nullptr_t, const weak_ordering right) noexcept
|
||||||
|
{
|
||||||
|
return right > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr bool operator>(std::nullptr_t, const weak_ordering right) noexcept
|
||||||
|
{
|
||||||
|
return right < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr bool operator<=(std::nullptr_t, const weak_ordering right) noexcept
|
||||||
|
{
|
||||||
|
return right >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] friend constexpr bool operator>=(std::nullptr_t, const weak_ordering right) noexcept
|
||||||
|
{
|
||||||
|
return right <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
signed char m_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr weak_ordering weak_ordering::less{static_cast<signed char>(-1)};
|
||||||
|
inline constexpr weak_ordering weak_ordering::equivalent{static_cast<signed char>(0)};
|
||||||
|
inline constexpr weak_ordering weak_ordering::greater{static_cast<signed char>(1)};
|
||||||
|
|
||||||
|
#endif // !_HAS_CXX20
|
||||||
|
|
||||||
|
#endif // defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
namespace wil
|
namespace wil
|
||||||
{
|
{
|
||||||
//! Strictly a function of the file system but this is the value for all known file system, NTFS, FAT.
|
//! 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.
|
//! CDFs has a limit of 254.
|
||||||
size_t const max_path_segment_length = 255;
|
constexpr size_t max_path_segment_length = 255;
|
||||||
|
|
||||||
//! Character length not including the null, MAX_PATH (260) includes the null.
|
//! Character length not including the null, MAX_PATH (260) includes the null.
|
||||||
size_t const max_path_length = 259;
|
constexpr size_t max_path_length = 259;
|
||||||
|
|
||||||
//! 32743 Character length not including the null. This is a system defined limit.
|
//! 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"
|
//! 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.
|
//! It will be 25 when there are more than 9 disks.
|
||||||
size_t const max_extended_path_length = 0x7FFF - 24;
|
constexpr size_t max_extended_path_length = 0x7FFF - 24;
|
||||||
|
|
||||||
//! For {guid} string form. Includes space for the null terminator.
|
//! For {guid} string form. Includes space for the null terminator.
|
||||||
size_t const guid_string_buffer_length = 39;
|
constexpr size_t guid_string_buffer_length = 39;
|
||||||
|
|
||||||
//! For {guid} string form. Not including the null terminator.
|
//! For {guid} string form. Not including the null terminator.
|
||||||
size_t const guid_string_length = 38;
|
constexpr size_t guid_string_length = 38;
|
||||||
|
|
||||||
|
#pragma region String and identifier comparisons
|
||||||
|
// Using CompareStringOrdinal functions:
|
||||||
|
//
|
||||||
|
// Indentifiers require a locale-less (ordinal), and often case-insensitive, comparison (filenames, registry keys, XML node names, etc).
|
||||||
|
// DO NOT use locale-sensitive (lexical) comparisons for resource identifiers (e.g.wcs*() functions in the CRT).
|
||||||
|
|
||||||
|
#if defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
|
||||||
|
|
||||||
|
namespace details
|
||||||
|
{
|
||||||
|
[[nodiscard]] inline int CompareStringOrdinal(std::wstring_view left, std::wstring_view right, bool caseInsensitive) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
// Casting from size_t (unsigned) to int (signed) should be safe from overrun to a negative,
|
||||||
|
// merely truncating the string. CompareStringOrdinal should be resilient to negatives.
|
||||||
|
return ::CompareStringOrdinal(left.data(), static_cast<int>(left.size()), right.data(), static_cast<int>(right.size()), caseInsensitive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] inline wistd::weak_ordering compare_string_ordinal(std::wstring_view left, std::wstring_view right, bool caseInsensitive) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
switch (wil::details::CompareStringOrdinal(left, right, caseInsensitive))
|
||||||
|
{
|
||||||
|
case CSTR_LESS_THAN:
|
||||||
|
return wistd::weak_ordering::less;
|
||||||
|
case CSTR_GREATER_THAN:
|
||||||
|
return wistd::weak_ordering::greater;
|
||||||
|
default:
|
||||||
|
return wistd::weak_ordering::equivalent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS)
|
||||||
|
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
#pragma region FILETIME helpers
|
#pragma region FILETIME helpers
|
||||||
// FILETIME duration values. FILETIME is in 100 nanosecond units.
|
// FILETIME duration values. FILETIME is in 100 nanosecond units.
|
||||||
|
@ -56,36 +200,126 @@ namespace wil
|
||||||
|
|
||||||
namespace filetime
|
namespace filetime
|
||||||
{
|
{
|
||||||
inline unsigned long long to_int64(const FILETIME &ft)
|
constexpr unsigned long long to_int64(const FILETIME &ft) WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
|
#if __cpp_lib_bit_cast >= 201806L
|
||||||
|
return std::bit_cast<unsigned long long>(ft);
|
||||||
|
#else
|
||||||
// Cannot reinterpret_cast FILETIME* to unsigned long long*
|
// Cannot reinterpret_cast FILETIME* to unsigned long long*
|
||||||
// due to alignment differences.
|
// due to alignment differences.
|
||||||
return (static_cast<unsigned long long>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
|
return (static_cast<unsigned long long>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline FILETIME from_int64(unsigned long long i64)
|
__WI_CONSTEXPR_BIT_CAST FILETIME from_int64(unsigned long long i64) WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
|
#if __cpp_lib_bit_cast >= 201806L
|
||||||
|
return std::bit_cast<FILETIME>(i64);
|
||||||
|
#else
|
||||||
static_assert(sizeof(i64) == sizeof(FILETIME), "sizes don't match");
|
static_assert(sizeof(i64) == sizeof(FILETIME), "sizes don't match");
|
||||||
static_assert(__alignof(unsigned long long) >= __alignof(FILETIME), "alignment not compatible with type pun");
|
static_assert(__alignof(unsigned long long) >= __alignof(FILETIME), "alignment not compatible with type pun");
|
||||||
return *reinterpret_cast<FILETIME *>(&i64);
|
return *reinterpret_cast<FILETIME *>(&i64);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline FILETIME add(_In_ FILETIME const &ft, long long delta)
|
__WI_CONSTEXPR_BIT_CAST FILETIME add(_In_ FILETIME const &ft, long long delta100ns) WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
return from_int64(to_int64(ft) + delta);
|
return from_int64(to_int64(ft) + delta100ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool is_empty(const FILETIME &ft)
|
constexpr bool is_empty(const FILETIME &ft) WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
return (ft.dwHighDateTime == 0) && (ft.dwLowDateTime == 0);
|
return (ft.dwHighDateTime == 0) && (ft.dwLowDateTime == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline FILETIME get_system_time()
|
inline FILETIME get_system_time() WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
FILETIME ft;
|
FILETIME ft;
|
||||||
GetSystemTimeAsFileTime(&ft);
|
GetSystemTimeAsFileTime(&ft);
|
||||||
return ft;
|
return ft;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert time as units of 100 nanoseconds to milliseconds. Fractional milliseconds are truncated.
|
||||||
|
constexpr unsigned long long convert_100ns_to_msec(unsigned long long time100ns) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
return time100ns / filetime_duration::one_millisecond;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert time as milliseconds to units of 100 nanoseconds.
|
||||||
|
constexpr unsigned long long convert_msec_to_100ns(unsigned long long timeMsec) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
return timeMsec * filetime_duration::one_millisecond;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_APISETREALTIME_) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7)
|
||||||
|
/// Returns the current unbiased interrupt-time count, in units of 100 nanoseconds. The unbiased interrupt-time count does not include time the system spends in sleep or hibernation.
|
||||||
|
///
|
||||||
|
/// This API avoids prematurely shortcircuiting timing loops due to system sleep/hibernation.
|
||||||
|
///
|
||||||
|
/// This is equivalent to GetTickCount64() except it returns units of 100 nanoseconds instead of milliseconds, and it doesn't include time the system spends in sleep or hibernation.
|
||||||
|
/// For example
|
||||||
|
///
|
||||||
|
/// start = GetTickCount64();
|
||||||
|
/// hibernate();
|
||||||
|
/// ...wake from hibernation 30 minutes later...;
|
||||||
|
/// elapsed = GetTickCount64() - start;
|
||||||
|
/// // elapsed = 30min
|
||||||
|
///
|
||||||
|
/// Do the same using unbiased interrupt-time and elapsed is 0 (or nearly so).
|
||||||
|
///
|
||||||
|
/// @note This is identical to QueryUnbiasedInterruptTime() but returns the value as a return value (rather than an out parameter).
|
||||||
|
/// @see https://msdn.microsoft.com/en-us/library/windows/desktop/ee662307(v=vs.85).aspx
|
||||||
|
inline unsigned long long QueryUnbiasedInterruptTimeAs100ns() WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
ULONGLONG now{};
|
||||||
|
QueryUnbiasedInterruptTime(&now);
|
||||||
|
return now;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current unbiased interrupt-time count, in units of milliseconds. The unbiased interrupt-time count does not include time the system spends in sleep or hibernation.
|
||||||
|
/// @see QueryUnbiasedInterruptTimeAs100ns
|
||||||
|
inline unsigned long long QueryUnbiasedInterruptTimeAsMSec() WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
return convert_100ns_to_msec(QueryUnbiasedInterruptTimeAs100ns());
|
||||||
|
}
|
||||||
|
#endif // _APISETREALTIME_
|
||||||
|
}
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
#pragma region RECT helpers
|
||||||
|
template<typename rect_type>
|
||||||
|
constexpr auto rect_width(rect_type const& rect)
|
||||||
|
{
|
||||||
|
return rect.right - rect.left;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename rect_type>
|
||||||
|
constexpr auto rect_height(rect_type const& rect)
|
||||||
|
{
|
||||||
|
return rect.bottom - rect.top;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename rect_type>
|
||||||
|
constexpr auto rect_is_empty(rect_type const& rect)
|
||||||
|
{
|
||||||
|
return (rect.left >= rect.right) || (rect.top >= rect.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename rect_type, typename point_type>
|
||||||
|
constexpr auto rect_contains_point(rect_type const& rect, point_type const& point)
|
||||||
|
{
|
||||||
|
return (point.x >= rect.left) && (point.x < rect.right) && (point.y >= rect.top) && (point.y < rect.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename rect_type, typename length_type>
|
||||||
|
constexpr rect_type rect_from_size(length_type x, length_type y, length_type width, length_type height)
|
||||||
|
{
|
||||||
|
rect_type rect;
|
||||||
|
rect.left = x;
|
||||||
|
rect.top = y;
|
||||||
|
rect.right = x + width;
|
||||||
|
rect.bottom = y + height;
|
||||||
|
return rect;
|
||||||
}
|
}
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
|
@ -95,7 +329,7 @@ namespace wil
|
||||||
// Adjust stackBufferLength based on typical result sizes to optimize use and
|
// Adjust stackBufferLength based on typical result sizes to optimize use and
|
||||||
// to test the boundary cases.
|
// to test the boundary cases.
|
||||||
template <typename string_type, size_t stackBufferLength = 256>
|
template <typename string_type, size_t stackBufferLength = 256>
|
||||||
HRESULT AdaptFixedSizeToAllocatedResult(string_type& result, wistd::function<HRESULT(PWSTR, size_t, size_t*)> callback)
|
HRESULT AdaptFixedSizeToAllocatedResult(string_type& result, wistd::function<HRESULT(PWSTR, size_t, size_t*)> callback) WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
details::string_maker<string_type> maker;
|
details::string_maker<string_type> maker;
|
||||||
|
|
||||||
|
@ -112,14 +346,24 @@ namespace wil
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Did not fit in the stack allocated buffer, need to do 2 phase construction.
|
// 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.
|
// May need to loop more than once if external conditions cause the value to change.
|
||||||
RETURN_IF_FAILED(maker.make(nullptr, valueLengthNeededWithNull - 1));
|
size_t bufferLength;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
bufferLength = valueLengthNeededWithNull;
|
||||||
|
// bufferLength includes the null so subtract that as make() will add space for it.
|
||||||
|
RETURN_IF_FAILED(maker.make(nullptr, bufferLength - 1));
|
||||||
|
|
||||||
size_t secondLength{};
|
RETURN_IF_FAILED_EXPECTED(callback(maker.buffer(), bufferLength, &valueLengthNeededWithNull));
|
||||||
RETURN_IF_FAILED(callback(maker.buffer(), valueLengthNeededWithNull, &secondLength));
|
WI_ASSERT(valueLengthNeededWithNull > 0);
|
||||||
|
|
||||||
// Ensure callback produces consistent result.
|
// If the value shrunk, then adjust the string to trim off the excess buffer.
|
||||||
FAIL_FAST_IF(valueLengthNeededWithNull != secondLength);
|
if (valueLengthNeededWithNull < bufferLength)
|
||||||
|
{
|
||||||
|
RETURN_IF_FAILED(maker.trim_at_existing_null(valueLengthNeededWithNull - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (valueLengthNeededWithNull > bufferLength);
|
||||||
}
|
}
|
||||||
result = maker.release();
|
result = maker.release();
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
@ -167,9 +411,6 @@ namespace wil
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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>
|
template <typename string_type, size_t stackBufferLength = 256>
|
||||||
HRESULT QueryFullProcessImageNameW(HANDLE processHandle, _In_ DWORD flags, string_type& result) WI_NOEXCEPT
|
HRESULT QueryFullProcessImageNameW(HANDLE processHandle, _In_ DWORD flags, string_type& result) WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
|
@ -179,8 +420,9 @@ namespace wil
|
||||||
DWORD lengthToUse = static_cast<DWORD>(valueLength);
|
DWORD lengthToUse = static_cast<DWORD>(valueLength);
|
||||||
BOOL const success = ::QueryFullProcessImageNameW(processHandle, flags, value, &lengthToUse);
|
BOOL const success = ::QueryFullProcessImageNameW(processHandle, flags, value, &lengthToUse);
|
||||||
RETURN_LAST_ERROR_IF((success == FALSE) && (::GetLastError() != ERROR_INSUFFICIENT_BUFFER));
|
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;
|
// On success, return the amount used; on failure, try doubling
|
||||||
|
*valueLengthNeededWithNul = success ? (lengthToUse + 1) : (lengthToUse * 2);
|
||||||
return S_OK;
|
return S_OK;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -201,13 +443,11 @@ namespace wil
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Looks up the environment variable 'key' and fails if it is not found.
|
/** Looks up the environment variable 'key' and fails if it is not found. */
|
||||||
'key' should not have '%' prefix and suffix.
|
template <typename string_type, size_t initialBufferLength = 128>
|
||||||
Dangerous since environment variable generally are optional. */
|
|
||||||
template <typename string_type>
|
|
||||||
inline HRESULT GetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
|
inline HRESULT GetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
return wil::AdaptFixedSizeToAllocatedResult(result,
|
return wil::AdaptFixedSizeToAllocatedResult<string_type, initialBufferLength>(result,
|
||||||
[&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
|
[&](_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
|
// If the function succeeds, the return value is the number of characters stored in the buffer
|
||||||
|
@ -232,12 +472,11 @@ namespace wil
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Looks up the environment variable 'key' and returns null if it is not found.
|
/** 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, size_t initialBufferLength = 128>
|
||||||
template <typename string_type>
|
|
||||||
HRESULT TryGetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
|
HRESULT TryGetEnvironmentVariableW(_In_ PCWSTR key, string_type& result) WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
const auto hr = wil::GetEnvironmentVariableW<string_type>(key, result);
|
const auto hr = wil::GetEnvironmentVariableW<string_type, initialBufferLength>(key, result);
|
||||||
RETURN_HR_IF(hr, FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND)));
|
RETURN_HR_IF(hr, FAILED(hr) && (hr != HRESULT_FROM_WIN32(ERROR_ENVVAR_NOT_FOUND)));
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
@ -245,68 +484,44 @@ namespace wil
|
||||||
/** Retrieves the fully qualified path for the file containing the specified module loaded
|
/** Retrieves the fully qualified path for the file containing the specified module loaded
|
||||||
by a given process. Note GetModuleFileNameExW is a macro.*/
|
by a given process. Note GetModuleFileNameExW is a macro.*/
|
||||||
template <typename string_type, size_t initialBufferLength = 128>
|
template <typename string_type, size_t initialBufferLength = 128>
|
||||||
HRESULT GetModuleFileNameExW(_In_opt_ HANDLE process, _In_opt_ HMODULE module, string_type& path)
|
HRESULT GetModuleFileNameExW(_In_opt_ HANDLE process, _In_opt_ HMODULE module, string_type& path) WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
// initialBufferLength is a template parameter to allow for testing. It creates some waste for
|
auto adapter = [&](_Out_writes_(valueLength) PWSTR value, size_t valueLength, _Out_ size_t* valueLengthNeededWithNul) -> HRESULT
|
||||||
// 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;
|
DWORD copiedCount;
|
||||||
|
size_t valueUsedWithNul;
|
||||||
bool copyFailed;
|
bool copyFailed;
|
||||||
bool copySucceededWithNoTruncation;
|
bool copySucceededWithNoTruncation;
|
||||||
|
|
||||||
if (process != nullptr)
|
if (process != nullptr)
|
||||||
{
|
{
|
||||||
// GetModuleFileNameExW truncates and provides no error or other indication it has done so.
|
// 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.
|
// The only way to be sure it didn't truncate is if it didn't need the whole buffer. The
|
||||||
copiedCount = ::GetModuleFileNameExW(process, module, maker.buffer(), static_cast<DWORD>(lengthWithNull));
|
// count copied to the buffer includes the nul-character as well.
|
||||||
|
copiedCount = ::GetModuleFileNameExW(process, module, value, static_cast<DWORD>(valueLength));
|
||||||
|
valueUsedWithNul = copiedCount + 1;
|
||||||
copyFailed = (0 == copiedCount);
|
copyFailed = (0 == copiedCount);
|
||||||
copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull - 1);
|
copySucceededWithNoTruncation = !copyFailed && (copiedCount < valueLength - 1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// In cases of insufficient buffer, GetModuleFileNameW will return a value equal to lengthWithNull
|
// In cases of insufficient buffer, GetModuleFileNameW will return a value equal to lengthWithNull
|
||||||
// and set the last error to ERROR_INSUFFICIENT_BUFFER.
|
// and set the last error to ERROR_INSUFFICIENT_BUFFER. The count returned does not include
|
||||||
copiedCount = ::GetModuleFileNameW(module, maker.buffer(), static_cast<DWORD>(lengthWithNull));
|
// the nul-character
|
||||||
|
copiedCount = ::GetModuleFileNameW(module, value, static_cast<DWORD>(valueLength));
|
||||||
|
valueUsedWithNul = copiedCount + 1;
|
||||||
copyFailed = (0 == copiedCount);
|
copyFailed = (0 == copiedCount);
|
||||||
copySucceededWithNoTruncation = !copyFailed && (copiedCount < lengthWithNull);
|
copySucceededWithNoTruncation = !copyFailed && (copiedCount < valueLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (copyFailed)
|
RETURN_LAST_ERROR_IF(copyFailed);
|
||||||
{
|
|
||||||
RETURN_LAST_ERROR();
|
// When the copy truncated, request another try with more space.
|
||||||
}
|
*valueLengthNeededWithNul = copySucceededWithNoTruncation ? valueUsedWithNul : (valueLength * 2);
|
||||||
else if (copySucceededWithNoTruncation)
|
|
||||||
{
|
|
||||||
path = maker.release();
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
};
|
||||||
|
|
||||||
WI_ASSERT((process != nullptr) || (::GetLastError() == ERROR_INSUFFICIENT_BUFFER));
|
return wil::AdaptFixedSizeToAllocatedResult<string_type, initialBufferLength>(path, wistd::move(adapter));
|
||||||
|
|
||||||
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.
|
/** Retrieves the fully qualified path for the file that contains the specified module.
|
||||||
|
@ -314,7 +529,7 @@ namespace wil
|
||||||
same format that was specified when the module was loaded. Therefore, the path can be a
|
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 '\\?\'. */
|
long or short file name, and can have the prefix '\\?\'. */
|
||||||
template <typename string_type, size_t initialBufferLength = 128>
|
template <typename string_type, size_t initialBufferLength = 128>
|
||||||
HRESULT GetModuleFileNameW(HMODULE module, string_type& path)
|
HRESULT GetModuleFileNameW(HMODULE module, string_type& path) WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
return wil::GetModuleFileNameExW<string_type, initialBufferLength>(nullptr, module, path);
|
return wil::GetModuleFileNameExW<string_type, initialBufferLength>(nullptr, module, path);
|
||||||
}
|
}
|
||||||
|
@ -357,50 +572,134 @@ namespace wil
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Looks up the environment variable 'key' and fails if it is not found.
|
/** Looks up the environment variable 'key' and fails if it is not found. */
|
||||||
'key' should not have '%' prefix and suffix.
|
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
|
||||||
Dangerous since environment variable generally are optional. */
|
|
||||||
template <typename string_type = wil::unique_cotaskmem_string>
|
|
||||||
string_type GetEnvironmentVariableW(_In_ PCWSTR key)
|
string_type GetEnvironmentVariableW(_In_ PCWSTR key)
|
||||||
{
|
{
|
||||||
string_type result;
|
string_type result;
|
||||||
THROW_IF_FAILED(wil::GetEnvironmentVariableW<string_type>(key, result));
|
THROW_IF_FAILED((wil::GetEnvironmentVariableW<string_type, initialBufferLength>(key, result)));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Looks up the environment variable 'key' and returns null if it is not found.
|
/** 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, size_t initialBufferLength = 128>
|
||||||
template <typename string_type = wil::unique_cotaskmem_string>
|
|
||||||
string_type TryGetEnvironmentVariableW(_In_ PCWSTR key)
|
string_type TryGetEnvironmentVariableW(_In_ PCWSTR key)
|
||||||
{
|
{
|
||||||
string_type result;
|
string_type result;
|
||||||
THROW_IF_FAILED(wil::TryGetEnvironmentVariableW<string_type>(key, result));
|
THROW_IF_FAILED((wil::TryGetEnvironmentVariableW<string_type, initialBufferLength>(key, result)));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename string_type = wil::unique_cotaskmem_string>
|
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
|
||||||
string_type GetModuleFileNameW(HMODULE module)
|
string_type GetModuleFileNameW(HMODULE module = nullptr /* current process module */)
|
||||||
{
|
{
|
||||||
string_type result;
|
string_type result;
|
||||||
THROW_IF_FAILED(wil::GetModuleFileNameW(module, result));
|
THROW_IF_FAILED((wil::GetModuleFileNameW<string_type, initialBufferLength>(module, result)));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename string_type = wil::unique_cotaskmem_string>
|
template <typename string_type = wil::unique_cotaskmem_string, size_t initialBufferLength = 128>
|
||||||
string_type GetModuleFileNameExW(HANDLE process, HMODULE module)
|
string_type GetModuleFileNameExW(HANDLE process, HMODULE module)
|
||||||
{
|
{
|
||||||
string_type result;
|
string_type result;
|
||||||
THROW_IF_FAILED(wil::GetModuleFileNameExW(process, module, result));
|
THROW_IF_FAILED((wil::GetModuleFileNameExW<string_type, initialBufferLength>(process, module, result)));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename string_type = wil::unique_cotaskmem_string, size_t stackBufferLength = 256>
|
||||||
|
string_type QueryFullProcessImageNameW(HANDLE processHandle = GetCurrentProcess(), DWORD flags = 0)
|
||||||
|
{
|
||||||
|
string_type result;
|
||||||
|
THROW_IF_FAILED((wil::QueryFullProcessImageNameW<string_type, stackBufferLength>(processHandle, flags, result)));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
|
||||||
|
|
||||||
|
// Lookup a DWORD value under HKLM\...\Image File Execution Options\<current process name>
|
||||||
|
inline DWORD GetCurrentProcessExecutionOption(PCWSTR valueName, DWORD defaultValue = 0)
|
||||||
|
{
|
||||||
|
auto filePath = wil::GetModuleFileNameW<wil::unique_cotaskmem_string>();
|
||||||
|
if (auto lastSlash = wcsrchr(filePath.get(), L'\\'))
|
||||||
|
{
|
||||||
|
const auto fileName = lastSlash + 1;
|
||||||
|
auto keyPath = wil::str_concat<wil::unique_cotaskmem_string>(LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\)",
|
||||||
|
fileName);
|
||||||
|
DWORD value{}, sizeofValue = sizeof(value);
|
||||||
|
if (::RegGetValueW(HKEY_LOCAL_MACHINE, keyPath.get(), valueName,
|
||||||
|
#ifdef RRF_SUBKEY_WOW6464KEY
|
||||||
|
RRF_RT_REG_DWORD | RRF_SUBKEY_WOW6464KEY,
|
||||||
|
#else
|
||||||
|
RRF_RT_REG_DWORD,
|
||||||
|
#endif
|
||||||
|
nullptr, &value, &sizeofValue) == ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Waits for a debugger to attach to the current process based on registry configuration.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\explorer.exe
|
||||||
|
// WaitForDebuggerPresent=1
|
||||||
|
//
|
||||||
|
// REG_DWORD value of
|
||||||
|
// missing or 0 -> don't break
|
||||||
|
// 1 -> wait for the debugger, continue execution once it is attached
|
||||||
|
// 2 -> wait for the debugger, break here once attached.
|
||||||
|
inline void WaitForDebuggerPresent(bool checkRegistryConfig = true)
|
||||||
|
{
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
auto configValue = checkRegistryConfig ? GetCurrentProcessExecutionOption(L"WaitForDebuggerPresent") : 1;
|
||||||
|
if (configValue == 0)
|
||||||
|
{
|
||||||
|
return; // not configured, don't wait
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsDebuggerPresent())
|
||||||
|
{
|
||||||
|
if (configValue == 2)
|
||||||
|
{
|
||||||
|
DebugBreak(); // debugger attached, SHIFT+F11 to return to the caller
|
||||||
|
}
|
||||||
|
return; // debugger now attached, continue executing
|
||||||
|
}
|
||||||
|
Sleep(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/** Retrieve the HINSTANCE for the current DLL or EXE using this symbol that
|
/** 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
|
the linker provides for every module. This avoids the need for a global HINSTANCE variable
|
||||||
and provides access to this value for static libraries. */
|
and provides access to this value for static libraries. */
|
||||||
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||||||
inline HINSTANCE GetModuleInstanceHandle() { return reinterpret_cast<HINSTANCE>(&__ImageBase); }
|
inline HINSTANCE GetModuleInstanceHandle() WI_NOEXCEPT { return reinterpret_cast<HINSTANCE>(&__ImageBase); }
|
||||||
|
|
||||||
|
// GetModuleHandleExW was added to the app partition in version 22000 of the SDK
|
||||||
|
#if defined(NTDDI_WIN10_CO) ? \
|
||||||
|
WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) : \
|
||||||
|
WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
|
||||||
|
// Use this in threads that can outlive the object or API call that created them.
|
||||||
|
// Without this COM, or the API caller, can unload the DLL, resulting in a crash.
|
||||||
|
// It is very important that this be the first object created in the thread proc
|
||||||
|
// as when this runs down the thread exits and no destructors of objects created before
|
||||||
|
// it will run.
|
||||||
|
[[nodiscard]] inline auto get_module_reference_for_thread() noexcept
|
||||||
|
{
|
||||||
|
HMODULE thisModule{};
|
||||||
|
FAIL_FAST_IF(!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, L"", &thisModule));
|
||||||
|
return wil::scope_exit([thisModule]
|
||||||
|
{
|
||||||
|
FreeLibraryAndExitThread(thisModule, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// @cond
|
/// @cond
|
||||||
namespace details
|
namespace details
|
||||||
|
@ -410,19 +709,19 @@ namespace wil
|
||||||
INIT_ONCE& m_once;
|
INIT_ONCE& m_once;
|
||||||
unsigned long m_flags = INIT_ONCE_INIT_FAILED;
|
unsigned long m_flags = INIT_ONCE_INIT_FAILED;
|
||||||
public:
|
public:
|
||||||
init_once_completer(_In_ INIT_ONCE& once) : m_once(once)
|
init_once_completer(_In_ INIT_ONCE& once) WI_NOEXCEPT : m_once(once)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
|
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
|
||||||
void success()
|
void success() WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
m_flags = 0;
|
m_flags = 0;
|
||||||
}
|
}
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
|
|
||||||
~init_once_completer()
|
~init_once_completer() WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
::InitOnceComplete(&m_once, m_flags, nullptr);
|
::InitOnceComplete(&m_once, m_flags, nullptr);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
//*********************************************************
|
||||||
|
//
|
||||||
|
// 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_RESULTMACROS_INCLUDED
|
||||||
|
#define __WIL_WIN32_RESULTMACROS_INCLUDED
|
||||||
|
|
||||||
|
#include "result_macros.h"
|
||||||
|
|
||||||
|
// Helpers for return macros
|
||||||
|
#define __WIN32_RETURN_WIN32(error, str) __WI_SUPPRESS_4127_S do { const auto __error = (error); if (FAILED_WIN32(__error)) { __R_FN(Return_Win32)(__R_INFO(str) __error); } return __error; } __WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||||
|
#define __WIN32_RETURN_GLE_FAIL(str) return __R_FN(Win32_Return_GetLastError)(__R_INFO_ONLY(str))
|
||||||
|
|
||||||
|
FORCEINLINE long __WIN32_FROM_HRESULT(HRESULT hr)
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
return HRESULT_FACILITY(hr) == FACILITY_WIN32 ? HRESULT_CODE(hr) : hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
//*****************************************************************************
|
||||||
|
// Macros for returning failures as WIN32 error codes
|
||||||
|
//*****************************************************************************
|
||||||
|
|
||||||
|
// Always returns a known result (WIN32 error code) - always logs failures
|
||||||
|
#define WIN32_RETURN_WIN32(error) __WIN32_RETURN_WIN32(wil::verify_win32(error), #error)
|
||||||
|
#define WIN32_RETURN_LAST_ERROR() __WIN32_RETURN_GLE_FAIL(nullptr)
|
||||||
|
|
||||||
|
// Conditionally returns failures (WIN32 error code) - always logs failures
|
||||||
|
#define WIN32_RETURN_IF_WIN32_ERROR(error) __WI_SUPPRESS_4127_S do { const auto __errorRet = wil::verify_win32(error); if (FAILED_WIN32(__errorRet)) { __WIN32_RETURN_WIN32(__errorRet, #error); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||||
|
#define WIN32_RETURN_WIN32_IF(error, condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { __WIN32_RETURN_WIN32(wil::verify_win32(error), #condition); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||||
|
#define WIN32_RETURN_WIN32_IF_NULL(error, ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __WIN32_RETURN_WIN32(wil::verify_win32(error), #ptr); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||||
|
#define WIN32_RETURN_LAST_ERROR_IF(condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { __WIN32_RETURN_GLE_FAIL(#condition); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||||
|
#define WIN32_RETURN_LAST_ERROR_IF_NULL(ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { __WIN32_RETURN_GLE_FAIL(#ptr); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||||
|
|
||||||
|
// Conditionally returns failures (WIN32 error code) - use for failures that are expected in common use - failures are not logged - macros are only for control flow pattern
|
||||||
|
#define WIN32_RETURN_IF_WIN32_ERROR_EXPECTED(error) __WI_SUPPRESS_4127_S do { const auto __errorRet = wil::verify_win32(error); if (FAILED_WIN32(__errorRet)) { return __errorRet; }} __WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||||
|
#define WIN32_RETURN_WIN32_IF_EXPECTED(error, condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { return wil::verify_win32(error); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||||
|
#define WIN32_RETURN_WIN32_IF_NULL_EXPECTED(error, ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { return wil::verify_win32(error); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||||
|
#define WIN32_RETURN_LAST_ERROR_IF_EXPECTED(condition) __WI_SUPPRESS_4127_S do { if (wil::verify_bool(condition)) { return wil::verify_win32(wil::details::GetLastErrorFail()); }} __WI_SUPPRESS_4127_E while ((void)0, 0)
|
||||||
|
#define WIN32_RETURN_LAST_ERROR_IF_NULL_EXPECTED(ptr) __WI_SUPPRESS_4127_S do { if ((ptr) == nullptr) { return wil::verify_win32(wil::details::GetLastErrorFail()); }} __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 WIN32_RETURN_CAUGHT_EXCEPTION() return __R_FN(Win32_Return_CaughtException)(__R_INFO_ONLY(nullptr))
|
||||||
|
|
||||||
|
// Use these macros in place of a catch block to handle exceptions
|
||||||
|
#define WIN32_CATCH_RETURN() catch (...) { WIN32_RETURN_CAUGHT_EXCEPTION(); }
|
||||||
|
|
||||||
|
namespace wil
|
||||||
|
{
|
||||||
|
//*****************************************************************************
|
||||||
|
// Public Helpers that catch -- mostly only enabled when exceptions are enabled
|
||||||
|
//*****************************************************************************
|
||||||
|
|
||||||
|
// Win32ErrorFromCaughtException 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 a WIN32 error code. If an exception is of an unrecognized type
|
||||||
|
// the function will fail fast.
|
||||||
|
//
|
||||||
|
// try
|
||||||
|
// {
|
||||||
|
// // Code
|
||||||
|
// }
|
||||||
|
// catch (...)
|
||||||
|
// {
|
||||||
|
// status = wil::Win32ErrorFromCaughtException();
|
||||||
|
// }
|
||||||
|
_Always_(_Post_satisfies_(return > 0))
|
||||||
|
__declspec(noinline) inline long Win32ErrorFromCaughtException() WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
return __WIN32_FROM_HRESULT(ResultFromCaughtException());
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace details::__R_NS_NAME
|
||||||
|
{
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
__R_DIRECT_METHOD(long, Win32_Return_CaughtException)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
__R_FN_LOCALS;
|
||||||
|
return __WIN32_FROM_HRESULT(wil::details::ReportFailure_CaughtException<FailureType::Return>(__R_DIRECT_FN_CALL_ONLY));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__R_DIRECT_METHOD(long, Win32_Return_GetLastError)(__R_DIRECT_FN_PARAMS_ONLY) WI_NOEXCEPT
|
||||||
|
{
|
||||||
|
__R_FN_LOCALS;
|
||||||
|
return __WIN32_FROM_HRESULT(wil::details::ReportFailure_GetLastErrorHr<FailureType::Return>(__R_DIRECT_FN_CALL_ONLY));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __WIL_WIN32_RESULTMACROS_INCLUDED
|
|
@ -19,24 +19,40 @@
|
||||||
#include "result.h"
|
#include "result.h"
|
||||||
#include "com.h"
|
#include "com.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
|
#include <windows.foundation.h>
|
||||||
#include <windows.foundation.collections.h>
|
#include <windows.foundation.collections.h>
|
||||||
|
|
||||||
#ifdef __cplusplus_winrt
|
#ifdef __cplusplus_winrt
|
||||||
#include <collection.h> // bring in the CRT iterator for support for C++ CX code
|
#include <collection.h> // bring in the CRT iterator for support for C++ CX code
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
|
||||||
/// @cond
|
/// @cond
|
||||||
|
#if defined(WIL_ENABLE_EXCEPTIONS) && !defined(__WI_HAS_STD_LESS)
|
||||||
|
#ifdef __has_include
|
||||||
|
#if __has_include(<functional>)
|
||||||
|
#define __WI_HAS_STD_LESS 1
|
||||||
|
#include <functional>
|
||||||
|
#endif // Otherwise, not using STL; don't specialize std::less
|
||||||
|
#else
|
||||||
|
// Fall back to the old way of forward declaring std::less
|
||||||
|
#define __WI_HAS_STD_LESS 1
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable:4643) // Forward declaring '...' in namespace std is not permitted by the C++ Standard.
|
||||||
namespace std
|
namespace std
|
||||||
{
|
{
|
||||||
template<class _Elem, class _Traits, class _Alloc>
|
|
||||||
class basic_string;
|
|
||||||
|
|
||||||
template<class _Ty>
|
template<class _Ty>
|
||||||
struct less;
|
struct less;
|
||||||
}
|
}
|
||||||
/// @endcond
|
#pragma warning(pop)
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
#if defined(WIL_ENABLE_EXCEPTIONS) && defined(__has_include)
|
||||||
|
#if __has_include(<vector>)
|
||||||
|
#define __WI_HAS_STD_VECTOR 1
|
||||||
|
#include <vector>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
// This enables this code to be used in code that uses the ABI prefix or not.
|
// This enables this code to be used in code that uses the ABI prefix or not.
|
||||||
// Code using the public SDK and C++ CX code has the ABI prefix, windows internal
|
// Code using the public SDK and C++ CX code has the ABI prefix, windows internal
|
||||||
|
@ -50,10 +66,9 @@ namespace std
|
||||||
|
|
||||||
namespace wil
|
namespace wil
|
||||||
{
|
{
|
||||||
#ifdef _INC_TIME
|
|
||||||
// time_t is the number of 1 - second intervals since January 1, 1970.
|
// time_t is the number of 1 - second intervals since January 1, 1970.
|
||||||
long long const SecondsToStartOf1970 = 0x2b6109100;
|
constexpr long long SecondsToStartOf1970 = 0x2b6109100;
|
||||||
long long const HundredNanoSecondsInSecond = 10000000LL;
|
constexpr long long HundredNanoSecondsInSecond = 10000000LL;
|
||||||
|
|
||||||
inline __time64_t DateTime_to_time_t(ABI::Windows::Foundation::DateTime dateTime)
|
inline __time64_t DateTime_to_time_t(ABI::Windows::Foundation::DateTime dateTime)
|
||||||
{
|
{
|
||||||
|
@ -67,7 +82,6 @@ namespace wil
|
||||||
dateTime.UniversalTime = (timeT + SecondsToStartOf1970) * HundredNanoSecondsInSecond;
|
dateTime.UniversalTime = (timeT + SecondsToStartOf1970) * HundredNanoSecondsInSecond;
|
||||||
return dateTime;
|
return dateTime;
|
||||||
}
|
}
|
||||||
#endif // _INC_TIME
|
|
||||||
|
|
||||||
#pragma region HSTRING Helpers
|
#pragma region HSTRING Helpers
|
||||||
/// @cond
|
/// @cond
|
||||||
|
@ -145,16 +159,17 @@ namespace wil
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
// Overload for std::wstring, or at least things that behave like std::wstring, without adding a dependency
|
||||||
template<class TraitsT, class AllocT>
|
// on STL headers
|
||||||
static const wchar_t* get_buffer(
|
template <typename StringT>
|
||||||
const std::basic_string<wchar_t, TraitsT, AllocT>& str,
|
static wistd::enable_if_t<wistd::conjunction_v<
|
||||||
UINT32* length) WI_NOEXCEPT
|
wistd::is_same<const wchar_t*, decltype(wistd::declval<StringT>().c_str())>,
|
||||||
|
wistd::is_same<typename StringT::size_type, decltype(wistd::declval<StringT>().length())>>,
|
||||||
|
const wchar_t*> get_buffer(const StringT& str, UINT32* length) WI_NOEXCEPT
|
||||||
{
|
{
|
||||||
*length = static_cast<UINT32>(str.length());
|
*length = static_cast<UINT32>(str.length());
|
||||||
return str.c_str();
|
return str.c_str();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
template <typename LhsT, typename RhsT>
|
template <typename LhsT, typename RhsT>
|
||||||
static auto compare(LhsT&& lhs, RhsT&& rhs) ->
|
static auto compare(LhsT&& lhs, RhsT&& rhs) ->
|
||||||
|
@ -224,8 +239,8 @@ namespace wil
|
||||||
//! Detects if one or more embedded null is present in an HSTRING.
|
//! Detects if one or more embedded null is present in an HSTRING.
|
||||||
inline bool HasEmbeddedNull(_In_opt_ HSTRING value)
|
inline bool HasEmbeddedNull(_In_opt_ HSTRING value)
|
||||||
{
|
{
|
||||||
BOOL hasEmbeddedNull;
|
BOOL hasEmbeddedNull = FALSE;
|
||||||
WindowsStringHasEmbeddedNull(value, &hasEmbeddedNull);
|
(void)WindowsStringHasEmbeddedNull(value, &hasEmbeddedNull);
|
||||||
return hasEmbeddedNull != FALSE;
|
return hasEmbeddedNull != FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,14 +444,14 @@ namespace wil
|
||||||
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
|
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
|
||||||
struct type // T holder
|
struct type // T holder
|
||||||
{
|
{
|
||||||
type() {};
|
type() = default;
|
||||||
type(T&& value) : m_value(wistd::forward<T>(value)) {};
|
type(T&& value) : m_value(wistd::forward<T>(value)) {}
|
||||||
operator T() const { return m_value; }
|
operator T() const { return m_value; }
|
||||||
type& operator=(T&& value) { m_value = wistd::forward<T>(value); return *this; }
|
type& operator=(T&& value) { m_value = wistd::forward<T>(value); return *this; }
|
||||||
T Get() const { return m_value; }
|
T Get() const { return m_value; }
|
||||||
|
|
||||||
// Returning T&& to support move only types
|
// Returning T&& to support move only types
|
||||||
// In case of absense of T::operator=(T&&) a call to T::operator=(const T&) will happen
|
// In case of absence of T::operator=(T&&) a call to T::operator=(const T&) will happen
|
||||||
T&& Get() { return wistd::move(m_value); }
|
T&& Get() { return wistd::move(m_value); }
|
||||||
|
|
||||||
HRESULT CopyTo(T* result) const { *result = m_value; return S_OK; }
|
HRESULT CopyTo(T* result) const { *result = m_value; return S_OK; }
|
||||||
|
@ -730,7 +745,7 @@ namespace wil
|
||||||
vector_range_nothrow(const vector_range_nothrow&) = delete;
|
vector_range_nothrow(const vector_range_nothrow&) = delete;
|
||||||
vector_range_nothrow& operator=(const vector_range_nothrow&) = delete;
|
vector_range_nothrow& operator=(const vector_range_nothrow&) = delete;
|
||||||
|
|
||||||
vector_range_nothrow(vector_range_nothrow&& other) :
|
vector_range_nothrow(vector_range_nothrow&& other) WI_NOEXCEPT :
|
||||||
m_v(other.m_v), m_size(other.m_size), m_result(other.m_result), m_resultStorage(other.m_resultStorage),
|
m_v(other.m_v), m_size(other.m_size), m_result(other.m_result), m_resultStorage(other.m_resultStorage),
|
||||||
m_currentElement(wistd::move(other.m_currentElement))
|
m_currentElement(wistd::move(other.m_currentElement))
|
||||||
{
|
{
|
||||||
|
@ -985,6 +1000,39 @@ namespace wil
|
||||||
};
|
};
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
|
#if defined(__WI_HAS_STD_VECTOR)
|
||||||
|
/** Converts WinRT vectors to std::vector by requesting the collection's data in a single
|
||||||
|
operation. This can be more efficient in terms of IPC cost than iteratively processing it.
|
||||||
|
~~~
|
||||||
|
ComPtr<IVector<IPropertyValue*>> values = GetValues();
|
||||||
|
std::vector<ComPtr<IPropertyValue>> allData = wil::to_vector(values);
|
||||||
|
for (ComPtr<IPropertyValue> const& item : allData)
|
||||||
|
{
|
||||||
|
// use item
|
||||||
|
}
|
||||||
|
Can be used for ABI::Windows::Foundation::Collections::IVector<T> and
|
||||||
|
ABI::Windows::Foundation::Collections::IVectorView<T>
|
||||||
|
*/
|
||||||
|
template<typename VectorType> auto to_vector(VectorType* src)
|
||||||
|
{
|
||||||
|
using TResult = typename details::MapVectorResultType<VectorType>::type;
|
||||||
|
using TSmart = typename details::MapToSmartType<TResult>::type;
|
||||||
|
static_assert(sizeof(TResult) == sizeof(TSmart), "result and smart sizes are different");
|
||||||
|
std::vector<TSmart> output;
|
||||||
|
UINT32 expected = 0;
|
||||||
|
THROW_IF_FAILED(src->get_Size(&expected));
|
||||||
|
if (expected > 0)
|
||||||
|
{
|
||||||
|
output.resize(expected + 1);
|
||||||
|
UINT32 fetched = 0;
|
||||||
|
THROW_IF_FAILED(src->GetMany(0, static_cast<UINT32>(output.size()), reinterpret_cast<TResult*>(output.data()), &fetched));
|
||||||
|
THROW_HR_IF(E_CHANGED_STATE, fetched > expected);
|
||||||
|
output.resize(fetched);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#pragma region error code base IIterable<>
|
#pragma region error code base IIterable<>
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class iterable_range_nothrow
|
class iterable_range_nothrow
|
||||||
|
@ -998,7 +1046,7 @@ namespace wil
|
||||||
iterable_range_nothrow& operator=(const iterable_range_nothrow&) = delete;
|
iterable_range_nothrow& operator=(const iterable_range_nothrow&) = delete;
|
||||||
iterable_range_nothrow& operator=(iterable_range_nothrow &&) = delete;
|
iterable_range_nothrow& operator=(iterable_range_nothrow &&) = delete;
|
||||||
|
|
||||||
iterable_range_nothrow(iterable_range_nothrow&& other) :
|
iterable_range_nothrow(iterable_range_nothrow&& other) WI_NOEXCEPT :
|
||||||
m_iterator(wistd::move(other.m_iterator)), m_element(wistd::move(other.m_element)),
|
m_iterator(wistd::move(other.m_iterator)), m_element(wistd::move(other.m_element)),
|
||||||
m_resultStorage(other.m_resultStorage)
|
m_resultStorage(other.m_resultStorage)
|
||||||
{
|
{
|
||||||
|
@ -1274,9 +1322,9 @@ namespace details
|
||||||
// LastType<int, bool>::type boolValue;
|
// LastType<int, bool>::type boolValue;
|
||||||
template <typename... Ts> struct LastType
|
template <typename... Ts> struct LastType
|
||||||
{
|
{
|
||||||
template<typename T, typename... Ts> struct LastTypeOfTs
|
template<typename T, typename... OtherTs> struct LastTypeOfTs
|
||||||
{
|
{
|
||||||
typedef typename LastTypeOfTs<Ts...>::type type;
|
typedef typename LastTypeOfTs<OtherTs...>::type type;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T> struct LastTypeOfTs<T>
|
template<typename T> struct LastTypeOfTs<T>
|
||||||
|
@ -1284,8 +1332,8 @@ namespace details
|
||||||
typedef T type;
|
typedef T type;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts>
|
template<typename... OtherTs>
|
||||||
static typename LastTypeOfTs<Ts...>::type LastTypeOfTsFunc() {}
|
static typename LastTypeOfTs<OtherTs...>::type LastTypeOfTsFunc() {}
|
||||||
typedef decltype(LastTypeOfTsFunc<Ts...>()) type;
|
typedef decltype(LastTypeOfTsFunc<Ts...>()) type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1315,14 +1363,28 @@ namespace details
|
||||||
typedef wistd::remove_pointer_t<decltype(GetAsyncDelegateType(operation))> TIDelegate;
|
typedef wistd::remove_pointer_t<decltype(GetAsyncDelegateType(operation))> TIDelegate;
|
||||||
|
|
||||||
auto callback = Callback<Implements<RuntimeClassFlags<ClassicCom>, TIDelegate, TBaseAgility>>(
|
auto callback = Callback<Implements<RuntimeClassFlags<ClassicCom>, TIDelegate, TBaseAgility>>(
|
||||||
[func = wistd::forward<TFunction>(func)](TIOperation operation, AsyncStatus status) mutable -> HRESULT
|
[func = wistd::forward<TFunction>(func)](TIOperation operation, ABI::Windows::Foundation::AsyncStatus status) mutable -> HRESULT
|
||||||
{
|
{
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
if (status != AsyncStatus::Completed) // avoid a potentially costly marshaled QI / call if we completed successfully
|
if (status != ABI::Windows::Foundation::AsyncStatus::Completed) // avoid a potentially costly marshaled QI / call if we completed successfully
|
||||||
{
|
{
|
||||||
ComPtr<IAsyncInfo> asyncInfo;
|
// QI to the IAsyncInfo interface. While all operations implement this, it is
|
||||||
operation->QueryInterface(IID_PPV_ARGS(&asyncInfo)); // All must implement IAsyncInfo
|
// possible that the stub has disconnected, causing the QI to fail.
|
||||||
asyncInfo->get_ErrorCode(&hr);
|
ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
|
||||||
|
hr = operation->QueryInterface(IID_PPV_ARGS(&asyncInfo));
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
// Save the error code result in a temporary variable to allow us
|
||||||
|
// to also retrieve the result of the COM call. If the stub has
|
||||||
|
// disconnected, this call may fail.
|
||||||
|
HRESULT errorCode = E_UNEXPECTED;
|
||||||
|
hr = asyncInfo->get_ErrorCode(&errorCode);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
// Return the operations error code to the caller.
|
||||||
|
hr = errorCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return CallAndHandleErrors(func, hr);
|
return CallAndHandleErrors(func, hr);
|
||||||
|
@ -1340,21 +1402,35 @@ namespace details
|
||||||
typedef wistd::remove_pointer_t<decltype(GetAsyncDelegateType(operation))> TIDelegate;
|
typedef wistd::remove_pointer_t<decltype(GetAsyncDelegateType(operation))> TIDelegate;
|
||||||
|
|
||||||
auto callback = Callback<Implements<RuntimeClassFlags<ClassicCom>, TIDelegate, TBaseAgility>>(
|
auto callback = Callback<Implements<RuntimeClassFlags<ClassicCom>, TIDelegate, TBaseAgility>>(
|
||||||
[func = wistd::forward<TFunction>(func)](TIOperation operation, AsyncStatus status) mutable -> HRESULT
|
[func = wistd::forward<TFunction>(func)](TIOperation operation, ABI::Windows::Foundation::AsyncStatus status) mutable -> HRESULT
|
||||||
{
|
{
|
||||||
typename details::MapToSmartType<typename GetAbiType<typename wistd::remove_pointer<TIOperation>::type::TResult_complex>::type>::type result;
|
typename details::MapToSmartType<typename GetAbiType<typename wistd::remove_pointer<TIOperation>::type::TResult_complex>::type>::type result;
|
||||||
|
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
if (status == AsyncStatus::Completed)
|
// avoid a potentially costly marshaled QI / call if we completed successfully
|
||||||
|
if (status == ABI::Windows::Foundation::AsyncStatus::Completed)
|
||||||
{
|
{
|
||||||
hr = operation->GetResults(result.GetAddressOf());
|
hr = operation->GetResults(result.GetAddressOf());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// avoid a potentially costly marshaled QI / call if we completed successfully
|
// QI to the IAsyncInfo interface. While all operations implement this, it is
|
||||||
ComPtr<IAsyncInfo> asyncInfo;
|
// possible that the stub has disconnected, causing the QI to fail.
|
||||||
operation->QueryInterface(IID_PPV_ARGS(&asyncInfo)); // all must implement this
|
ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
|
||||||
asyncInfo->get_ErrorCode(&hr);
|
hr = operation->QueryInterface(IID_PPV_ARGS(&asyncInfo));
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
// Save the error code result in a temporary variable to allow us
|
||||||
|
// to also retrieve the result of the COM call. If the stub has
|
||||||
|
// disconnected, this call may fail.
|
||||||
|
HRESULT errorCode = E_UNEXPECTED;
|
||||||
|
hr = asyncInfo->get_ErrorCode(&errorCode);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
// Return the operations error code to the caller.
|
||||||
|
hr = errorCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return CallAndHandleErrors(func, hr, result.Get());
|
return CallAndHandleErrors(func, hr, result.Get());
|
||||||
|
@ -1378,7 +1454,7 @@ namespace details
|
||||||
RETURN_HR(m_completedEventHandle.create());
|
RETURN_HR(m_completedEventHandle.create());
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE Invoke(_In_ TIOperation, AsyncStatus status) override
|
HRESULT STDMETHODCALLTYPE Invoke(_In_ TIOperation, ABI::Windows::Foundation::AsyncStatus status) override
|
||||||
{
|
{
|
||||||
m_status = status;
|
m_status = status;
|
||||||
m_completedEventHandle.SetEvent();
|
m_completedEventHandle.SetEvent();
|
||||||
|
@ -1390,13 +1466,13 @@ namespace details
|
||||||
return m_completedEventHandle.get();
|
return m_completedEventHandle.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncStatus GetStatus() const
|
ABI::Windows::Foundation::AsyncStatus GetStatus() const
|
||||||
{
|
{
|
||||||
return m_status;
|
return m_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
volatile AsyncStatus m_status = AsyncStatus::Started;
|
volatile ABI::Windows::Foundation::AsyncStatus m_status = ABI::Windows::Foundation::AsyncStatus::Started;
|
||||||
wil::unique_event_nothrow m_completedEventHandle;
|
wil::unique_event_nothrow m_completedEventHandle;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1419,12 +1495,25 @@ namespace details
|
||||||
}
|
}
|
||||||
RETURN_IF_FAILED(hr);
|
RETURN_IF_FAILED(hr);
|
||||||
|
|
||||||
if (completedDelegate->GetStatus() != AsyncStatus::Completed)
|
if (completedDelegate->GetStatus() != ABI::Windows::Foundation::AsyncStatus::Completed)
|
||||||
{
|
{
|
||||||
Microsoft::WRL::ComPtr<IAsyncInfo> asyncInfo;
|
// QI to the IAsyncInfo interface. While all operations implement this, it is
|
||||||
operation->QueryInterface(IID_PPV_ARGS(&asyncInfo)); // all must implement this
|
// possible that the stub has disconnected, causing the QI to fail.
|
||||||
hr = E_UNEXPECTED;
|
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
|
||||||
asyncInfo->get_ErrorCode(&hr); // error return ignored, ok?
|
hr = operation->QueryInterface(IID_PPV_ARGS(&asyncInfo));
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
// Save the error code result in a temporary variable to allow us
|
||||||
|
// to also retrieve the result of the COM call. If the stub has
|
||||||
|
// disconnected, this call may fail.
|
||||||
|
HRESULT errorCode = E_UNEXPECTED;
|
||||||
|
hr = asyncInfo->get_ErrorCode(&errorCode);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
// Return the operations error code to the caller.
|
||||||
|
hr = errorCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
return hr; // leave it to the caller to log failures.
|
return hr; // leave it to the caller to log failures.
|
||||||
}
|
}
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
@ -1669,7 +1758,7 @@ namespace details
|
||||||
|
|
||||||
IFACEMETHODIMP put_Completed(ABI::Windows::Foundation::IAsyncOperationCompletedHandler<TResult>* competed) override
|
IFACEMETHODIMP put_Completed(ABI::Windows::Foundation::IAsyncOperationCompletedHandler<TResult>* competed) override
|
||||||
{
|
{
|
||||||
competed->Invoke(this, AsyncStatus::Completed);
|
competed->Invoke(this, ABI::Windows::Foundation::AsyncStatus::Completed);
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1708,7 +1797,7 @@ namespace details
|
||||||
public:
|
public:
|
||||||
IFACEMETHODIMP put_Completed(ABI::Windows::Foundation::IAsyncActionCompletedHandler* competed) override
|
IFACEMETHODIMP put_Completed(ABI::Windows::Foundation::IAsyncActionCompletedHandler* competed) override
|
||||||
{
|
{
|
||||||
competed->Invoke(this, AsyncStatus::Completed);
|
competed->Invoke(this, ABI::Windows::Foundation::AsyncStatus::Completed);
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1866,7 +1955,19 @@ public:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto resolvedSender = m_weakSender.Resolve<T>();
|
auto resolvedSender = [&]()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return m_weakSender.Resolve<T>();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
// Ignore RPC or other failures that are unavoidable in some cases
|
||||||
|
// matching wil::unique_winrt_event_token and winrt::event_revoker
|
||||||
|
return static_cast<T^>(nullptr);
|
||||||
|
}
|
||||||
|
}();
|
||||||
if (resolvedSender)
|
if (resolvedSender)
|
||||||
{
|
{
|
||||||
(resolvedSender->*m_removalFunction)(m_token);
|
(resolvedSender->*m_removalFunction)(m_token);
|
||||||
|
@ -2205,7 +2306,7 @@ struct ABI::Windows::Foundation::IAsyncOperationWithProgressCompletedHandler<ABI
|
||||||
#pragma pop_macro("ABI")
|
#pragma pop_macro("ABI")
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
#if __WI_HAS_STD_LESS
|
||||||
|
|
||||||
namespace std
|
namespace std
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
#define _WISTD_CONFIG_H_
|
#define _WISTD_CONFIG_H_
|
||||||
|
|
||||||
// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage
|
// 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
|
#include <cstddef> // For size_t and other necessary types
|
||||||
|
|
||||||
/// @cond
|
/// @cond
|
||||||
#if defined(_MSC_VER) && !defined(__clang__)
|
#if defined(_MSC_VER) && !defined(__clang__)
|
||||||
|
@ -107,6 +107,29 @@
|
||||||
# define __WI_LIBCPP_COMPILER_IBM
|
# define __WI_LIBCPP_COMPILER_IBM
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__WI_LIBCPP_COMPILER_MSVC)
|
||||||
|
#define __WI_PUSH_WARNINGS __pragma(warning(push))
|
||||||
|
#define __WI_POP_WARNINGS __pragma(warning(pop))
|
||||||
|
#elif defined(__WI_LIBCPP_COMPILER_CLANG)
|
||||||
|
#define __WI_PUSH_WARNINGS __pragma(clang diagnostic push)
|
||||||
|
#define __WI_POP_WARNINGS __pragma(clang diagnostic pop)
|
||||||
|
#else
|
||||||
|
#define __WI_PUSH_WARNINGS
|
||||||
|
#define __WI_POP_WARNINGS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __WI_LIBCPP_COMPILER_MSVC
|
||||||
|
#define __WI_MSVC_DISABLE_WARNING(id) __pragma(warning(disable: id))
|
||||||
|
#else
|
||||||
|
#define __WI_MSVC_DISABLE_WARNING(id)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __WI_LIBCPP_COMPILER_CLANG
|
||||||
|
#define __WI_CLANG_DISABLE_WARNING(warning) __pragma(clang diagnostic ignored #warning)
|
||||||
|
#else
|
||||||
|
#define __WI_CLANG_DISABLE_WARNING(warning)
|
||||||
|
#endif
|
||||||
|
|
||||||
// NOTE: MSVC, which is what we primarily target, is severly underrepresented in libc++ and checks such as
|
// 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
|
// __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
|
// instead modify all checks to be __WI_HAS_FEATURE_IS_UNION, etc., which provides the correct value for MSVC and falls
|
||||||
|
@ -448,7 +471,7 @@
|
||||||
|
|
||||||
namespace wistd // ("Windows Implementation" std)
|
namespace wistd // ("Windows Implementation" std)
|
||||||
{
|
{
|
||||||
typedef decltype(__nullptr) nullptr_t;
|
using nullptr_t = decltype(__nullptr);
|
||||||
|
|
||||||
template <class _T1, class _T2 = _T1>
|
template <class _T1, class _T2 = _T1>
|
||||||
struct __less
|
struct __less
|
||||||
|
@ -531,18 +554,18 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
template <class _Arg, class _Result>
|
template <class _Arg, class _Result>
|
||||||
struct __WI_LIBCPP_TEMPLATE_VIS unary_function
|
struct __WI_LIBCPP_TEMPLATE_VIS unary_function
|
||||||
{
|
{
|
||||||
typedef _Arg argument_type;
|
using argument_type = _Arg;
|
||||||
typedef _Result result_type;
|
using result_type = _Result;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class _Arg1, class _Arg2, class _Result>
|
template <class _Arg1, class _Arg2, class _Result>
|
||||||
struct __WI_LIBCPP_TEMPLATE_VIS binary_function
|
struct __WI_LIBCPP_TEMPLATE_VIS binary_function
|
||||||
{
|
{
|
||||||
typedef _Arg1 first_argument_type;
|
using first_argument_type = _Arg1;
|
||||||
typedef _Arg2 second_argument_type;
|
using second_argument_type = _Arg2;
|
||||||
typedef _Result result_type;
|
using result_type = _Result;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/// @endcond
|
/// @endcond
|
||||||
|
|
||||||
#endif _WISTD_CONFIG_H_
|
#endif // _WISTD_CONFIG_H_
|
||||||
|
|
|
@ -46,6 +46,7 @@
|
||||||
|
|
||||||
#pragma warning(push)
|
#pragma warning(push)
|
||||||
#pragma warning(disable: 4324)
|
#pragma warning(disable: 4324)
|
||||||
|
#pragma warning(disable: 4800)
|
||||||
|
|
||||||
/// @cond
|
/// @cond
|
||||||
namespace wistd // ("Windows Implementation" std)
|
namespace wistd // ("Windows Implementation" std)
|
||||||
|
@ -249,25 +250,28 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
return _Invoker::__call(__f_, wistd::forward<_ArgTypes>(__arg)...);
|
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
|
// '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
|
// 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).
|
// pointers (__base vtable takes an additional one).
|
||||||
static constexpr size_t __buffer_size = 13 * sizeof(void*);
|
constexpr const size_t __buffer_size = 13 * sizeof(void*);
|
||||||
|
|
||||||
typedef __function::__base<_Rp(_ArgTypes...)> __base;
|
} // __function
|
||||||
|
|
||||||
|
// NOTE: The extra 'alignas' here is to work around the x86 compiler bug mentioned in
|
||||||
|
// https://github.com/microsoft/STL/issues/1533 to force alignment on the stack
|
||||||
|
template<class _Rp, class ..._ArgTypes>
|
||||||
|
class __WI_LIBCPP_TEMPLATE_VIS __WI_ALIGNAS(typename aligned_storage<__function::__buffer_size>::type)
|
||||||
|
function<_Rp(_ArgTypes...)>
|
||||||
|
: public __function::__maybe_derive_from_unary_function<_Rp(_ArgTypes...)>,
|
||||||
|
public __function::__maybe_derive_from_binary_function<_Rp(_ArgTypes...)>
|
||||||
|
{
|
||||||
|
using __base = __function::__base<_Rp(_ArgTypes...)>;
|
||||||
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
||||||
typename aligned_storage<__buffer_size>::type __buf_;
|
typename aligned_storage<__function::__buffer_size>::type __buf_;
|
||||||
__base* __f_;
|
__base* __f_;
|
||||||
|
|
||||||
__WI_LIBCPP_NO_CFI static __base *__as_base(void *p) {
|
__WI_LIBCPP_NO_CFI static __base *__as_base(void *p) {
|
||||||
return reinterpret_cast<__base*>(p);
|
return static_cast<__base*>(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class _Fp, bool>
|
template <class _Fp, bool>
|
||||||
|
@ -281,7 +285,7 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
template <class _Fp>
|
template <class _Fp>
|
||||||
struct __callable_imp<_Fp, false>
|
struct __callable_imp<_Fp, false>
|
||||||
{
|
{
|
||||||
static const bool value = false;
|
static constexpr bool value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class _Fp>
|
template <class _Fp>
|
||||||
|
@ -296,11 +300,12 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
template <class _Fp>
|
template <class _Fp>
|
||||||
using _EnableIfCallable = typename enable_if<__callable<_Fp>::value>::type;
|
using _EnableIfCallable = typename enable_if<__callable<_Fp>::value>::type;
|
||||||
public:
|
public:
|
||||||
typedef _Rp result_type;
|
using result_type = _Rp;
|
||||||
|
|
||||||
// construct/copy/destroy:
|
// construct/copy/destroy:
|
||||||
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
__WI_LIBCPP_INLINE_VISIBILITY __WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
||||||
function() WI_NOEXCEPT : __f_(0) {}
|
function() WI_NOEXCEPT : __f_(0) {}
|
||||||
|
|
||||||
__WI_LIBCPP_INLINE_VISIBILITY
|
__WI_LIBCPP_INLINE_VISIBILITY
|
||||||
function(nullptr_t) WI_NOEXCEPT : __f_(0) {}
|
function(nullptr_t) WI_NOEXCEPT : __f_(0) {}
|
||||||
function(const function&);
|
function(const function&);
|
||||||
|
@ -340,7 +345,7 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
||||||
function<_Rp(_ArgTypes...)>::function(const function& __f)
|
function<_Rp(_ArgTypes...)>::function(const function& __f)
|
||||||
{
|
{
|
||||||
if (__f.__f_ == 0)
|
if (__f.__f_ == nullptr)
|
||||||
__f_ = 0;
|
__f_ = 0;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -353,7 +358,7 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS
|
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS __WI_LIBCPP_SUPPRESS_NOEXCEPT_ANALYSIS
|
||||||
function<_Rp(_ArgTypes...)>::function(function&& __f)
|
function<_Rp(_ArgTypes...)>::function(function&& __f)
|
||||||
{
|
{
|
||||||
if (__f.__f_ == 0)
|
if (__f.__f_ == nullptr)
|
||||||
__f_ = 0;
|
__f_ = 0;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -368,14 +373,14 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
template <class _Fp, class>
|
template <class _Fp, class>
|
||||||
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
__WI_LIBCPP_SUPPRESS_NONINIT_ANALYSIS
|
||||||
function<_Rp(_ArgTypes...)>::function(_Fp __f)
|
function<_Rp(_ArgTypes...)>::function(_Fp __f)
|
||||||
: __f_(0)
|
: __f_(nullptr)
|
||||||
{
|
{
|
||||||
if (__function::__not_null(__f))
|
if (__function::__not_null(__f))
|
||||||
{
|
{
|
||||||
typedef __function::__func<_Fp, _Rp(_ArgTypes...)> _FF;
|
typedef __function::__func<_Fp, _Rp(_ArgTypes...)> _FF;
|
||||||
static_assert(sizeof(_FF) <= sizeof(__buf_),
|
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.");
|
"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));
|
__f_ = ::new(static_cast<void*>(&__buf_)) _FF(wistd::move(__f));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -429,7 +434,7 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
typedef __function::__func<typename decay<_Fp>::type, _Rp(_ArgTypes...)> _FF;
|
typedef __function::__func<typename decay<_Fp>::type, _Rp(_ArgTypes...)> _FF;
|
||||||
static_assert(sizeof(_FF) <= sizeof(__buf_),
|
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.");
|
"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));
|
__f_ = ::new(static_cast<void*>(&__buf_)) _FF(wistd::move(__f));
|
||||||
}
|
}
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -483,7 +488,7 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
_Rp
|
_Rp
|
||||||
function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const
|
function<_Rp(_ArgTypes...)>::operator()(_ArgTypes... __arg) const
|
||||||
{
|
{
|
||||||
if (__f_ == 0)
|
if (__f_ == nullptr)
|
||||||
__throw_bad_function_call();
|
__throw_bad_function_call();
|
||||||
return (*__f_)(wistd::forward<_ArgTypes>(__arg)...);
|
return (*__f_)(wistd::forward<_ArgTypes>(__arg)...);
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,13 +60,13 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
template <class _Tp, class _Dp, bool = __has_pointer_type<_Dp>::value>
|
template <class _Tp, class _Dp, bool = __has_pointer_type<_Dp>::value>
|
||||||
struct __pointer_type
|
struct __pointer_type
|
||||||
{
|
{
|
||||||
typedef typename _Dp::pointer type;
|
using type = typename _Dp::pointer;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class _Tp, class _Dp>
|
template <class _Tp, class _Dp>
|
||||||
struct __pointer_type<_Tp, _Dp, false>
|
struct __pointer_type<_Tp, _Dp, false>
|
||||||
{
|
{
|
||||||
typedef _Tp* type;
|
using type = _Tp*;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // __pointer_type_imp
|
} // __pointer_type_imp
|
||||||
|
@ -74,16 +74,16 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
template <class _Tp, class _Dp>
|
template <class _Tp, class _Dp>
|
||||||
struct __pointer_type
|
struct __pointer_type
|
||||||
{
|
{
|
||||||
typedef typename __pointer_type_imp::__pointer_type<_Tp, typename remove_reference<_Dp>::type>::type type;
|
using type = typename __pointer_type_imp::__pointer_type<_Tp, typename remove_reference<_Dp>::type>::type;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class _Tp, int _Idx,
|
template <class _Tp, int _Idx,
|
||||||
bool _CanBeEmptyBase =
|
bool _CanBeEmptyBase =
|
||||||
is_empty<_Tp>::value && !__libcpp_is_final<_Tp>::value>
|
is_empty<_Tp>::value && !__libcpp_is_final<_Tp>::value>
|
||||||
struct __compressed_pair_elem {
|
struct __compressed_pair_elem {
|
||||||
typedef _Tp _ParamT;
|
using _ParamT = _Tp;
|
||||||
typedef _Tp& reference;
|
using reference = _Tp&;
|
||||||
typedef const _Tp& const_reference;
|
using const_reference = const _Tp&;
|
||||||
|
|
||||||
#ifndef __WI_LIBCPP_CXX03_LANG
|
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||||
__WI_LIBCPP_INLINE_VISIBILITY constexpr __compressed_pair_elem() : __value_() {}
|
__WI_LIBCPP_INLINE_VISIBILITY constexpr __compressed_pair_elem() : __value_() {}
|
||||||
|
@ -115,10 +115,10 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
|
|
||||||
template <class _Tp, int _Idx>
|
template <class _Tp, int _Idx>
|
||||||
struct __compressed_pair_elem<_Tp, _Idx, true> : private _Tp {
|
struct __compressed_pair_elem<_Tp, _Idx, true> : private _Tp {
|
||||||
typedef _Tp _ParamT;
|
using _ParamT = _Tp;
|
||||||
typedef _Tp& reference;
|
using reference = _Tp&;
|
||||||
typedef const _Tp& const_reference;
|
using const_reference = const _Tp&;
|
||||||
typedef _Tp __value_type;
|
using __value_type = _Tp;
|
||||||
|
|
||||||
#ifndef __WI_LIBCPP_CXX03_LANG
|
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||||
__WI_LIBCPP_INLINE_VISIBILITY constexpr __compressed_pair_elem() = default;
|
__WI_LIBCPP_INLINE_VISIBILITY constexpr __compressed_pair_elem() = default;
|
||||||
|
@ -149,10 +149,10 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
struct __second_tag {};
|
struct __second_tag {};
|
||||||
|
|
||||||
template <class _T1, class _T2>
|
template <class _T1, class _T2>
|
||||||
class __compressed_pair : private __compressed_pair_elem<_T1, 0>,
|
class __declspec(empty_bases) __compressed_pair : private __compressed_pair_elem<_T1, 0>,
|
||||||
private __compressed_pair_elem<_T2, 1> {
|
private __compressed_pair_elem<_T2, 1> {
|
||||||
typedef __compressed_pair_elem<_T1, 0> _Base1;
|
using _Base1 = __compressed_pair_elem<_T1, 0>;
|
||||||
typedef __compressed_pair_elem<_T2, 1> _Base2;
|
using _Base2 = __compressed_pair_elem<_T2, 1>;
|
||||||
|
|
||||||
// NOTE: This static assert should never fire because __compressed_pair
|
// NOTE: This static assert should never fire because __compressed_pair
|
||||||
// is *almost never* used in a scenario where it's possible for T1 == T2.
|
// is *almost never* used in a scenario where it's possible for T1 == T2.
|
||||||
|
@ -271,7 +271,7 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
__WI_LIBCPP_INLINE_VISIBILITY
|
__WI_LIBCPP_INLINE_VISIBILITY
|
||||||
default_delete(const default_delete<_Up>&,
|
default_delete(const default_delete<_Up>&,
|
||||||
typename enable_if<is_convertible<_Up*, _Tp*>::value>::type* =
|
typename enable_if<is_convertible<_Up*, _Tp*>::value>::type* =
|
||||||
0) WI_NOEXCEPT {}
|
nullptr) WI_NOEXCEPT {}
|
||||||
|
|
||||||
__WI_LIBCPP_INLINE_VISIBILITY void operator()(_Tp* __ptr) const WI_NOEXCEPT {
|
__WI_LIBCPP_INLINE_VISIBILITY void operator()(_Tp* __ptr) const WI_NOEXCEPT {
|
||||||
static_assert(sizeof(_Tp) > 0,
|
static_assert(sizeof(_Tp) > 0,
|
||||||
|
@ -299,7 +299,7 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
template <class _Up>
|
template <class _Up>
|
||||||
__WI_LIBCPP_INLINE_VISIBILITY
|
__WI_LIBCPP_INLINE_VISIBILITY
|
||||||
default_delete(const default_delete<_Up[]>&,
|
default_delete(const default_delete<_Up[]>&,
|
||||||
typename _EnableIfConvertible<_Up>::type* = 0) WI_NOEXCEPT {}
|
typename _EnableIfConvertible<_Up>::type* = nullptr) WI_NOEXCEPT {}
|
||||||
|
|
||||||
template <class _Up>
|
template <class _Up>
|
||||||
__WI_LIBCPP_INLINE_VISIBILITY
|
__WI_LIBCPP_INLINE_VISIBILITY
|
||||||
|
@ -319,32 +319,32 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
template <class _Deleter>
|
template <class _Deleter>
|
||||||
struct __unique_ptr_deleter_sfinae {
|
struct __unique_ptr_deleter_sfinae {
|
||||||
static_assert(!is_reference<_Deleter>::value, "incorrect specialization");
|
static_assert(!is_reference<_Deleter>::value, "incorrect specialization");
|
||||||
typedef const _Deleter& __lval_ref_type;
|
using __lval_ref_type = const _Deleter&;
|
||||||
typedef _Deleter&& __good_rval_ref_type;
|
using __good_rval_ref_type = _Deleter&&;
|
||||||
typedef true_type __enable_rval_overload;
|
using __enable_rval_overload = true_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class _Deleter>
|
template <class _Deleter>
|
||||||
struct __unique_ptr_deleter_sfinae<_Deleter const&> {
|
struct __unique_ptr_deleter_sfinae<_Deleter const&> {
|
||||||
typedef const _Deleter& __lval_ref_type;
|
using __lval_ref_type = const _Deleter&;
|
||||||
typedef const _Deleter&& __bad_rval_ref_type;
|
using __bad_rval_ref_type = const _Deleter&&;
|
||||||
typedef false_type __enable_rval_overload;
|
using __enable_rval_overload = false_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class _Deleter>
|
template <class _Deleter>
|
||||||
struct __unique_ptr_deleter_sfinae<_Deleter&> {
|
struct __unique_ptr_deleter_sfinae<_Deleter&> {
|
||||||
typedef _Deleter& __lval_ref_type;
|
using __lval_ref_type = _Deleter&;
|
||||||
typedef _Deleter&& __bad_rval_ref_type;
|
using __bad_rval_ref_type = _Deleter&&;
|
||||||
typedef false_type __enable_rval_overload;
|
using __enable_rval_overload = false_type;
|
||||||
};
|
};
|
||||||
#endif // !defined(__WI_LIBCPP_CXX03_LANG)
|
#endif // !defined(__WI_LIBCPP_CXX03_LANG)
|
||||||
|
|
||||||
template <class _Tp, class _Dp = default_delete<_Tp> >
|
template <class _Tp, class _Dp = default_delete<_Tp> >
|
||||||
class __WI_LIBCPP_TEMPLATE_VIS unique_ptr {
|
class __WI_LIBCPP_TEMPLATE_VIS unique_ptr {
|
||||||
public:
|
public:
|
||||||
typedef _Tp element_type;
|
using element_type = _Tp;
|
||||||
typedef _Dp deleter_type;
|
using deleter_type = _Dp;
|
||||||
typedef typename __pointer_type<_Tp, deleter_type>::type pointer;
|
using pointer = typename __pointer_type<_Tp, deleter_type>::type;
|
||||||
|
|
||||||
static_assert(!is_rvalue_reference<deleter_type>::value,
|
static_assert(!is_rvalue_reference<deleter_type>::value,
|
||||||
"the specified deleter type cannot be an rvalue reference");
|
"the specified deleter type cannot be an rvalue reference");
|
||||||
|
@ -355,7 +355,7 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
struct __nat { int __for_bool_; };
|
struct __nat { int __for_bool_; };
|
||||||
|
|
||||||
#ifndef __WI_LIBCPP_CXX03_LANG
|
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||||
typedef __unique_ptr_deleter_sfinae<_Dp> _DeleterSFINAE;
|
using _DeleterSFINAE = __unique_ptr_deleter_sfinae<_Dp>;
|
||||||
|
|
||||||
template <bool _Dummy>
|
template <bool _Dummy>
|
||||||
using _LValRefType =
|
using _LValRefType =
|
||||||
|
@ -582,9 +582,9 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
template <class _Tp, class _Dp>
|
template <class _Tp, class _Dp>
|
||||||
class __WI_LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp> {
|
class __WI_LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp> {
|
||||||
public:
|
public:
|
||||||
typedef _Tp element_type;
|
using element_type = _Tp;
|
||||||
typedef _Dp deleter_type;
|
using deleter_type = _Dp;
|
||||||
typedef typename __pointer_type<_Tp, deleter_type>::type pointer;
|
using pointer = typename __pointer_type<_Tp, deleter_type>::type;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
__compressed_pair<pointer, deleter_type> __ptr_;
|
__compressed_pair<pointer, deleter_type> __ptr_;
|
||||||
|
@ -602,7 +602,7 @@ namespace wistd // ("Windows Implementation" std)
|
||||||
{};
|
{};
|
||||||
|
|
||||||
#ifndef __WI_LIBCPP_CXX03_LANG
|
#ifndef __WI_LIBCPP_CXX03_LANG
|
||||||
typedef __unique_ptr_deleter_sfinae<_Dp> _DeleterSFINAE;
|
using _DeleterSFINAE = __unique_ptr_deleter_sfinae<_Dp>;
|
||||||
|
|
||||||
template <bool _Dummy>
|
template <bool _Dummy>
|
||||||
using _LValRefType =
|
using _LValRefType =
|
||||||
|
|
|
@ -14,6 +14,11 @@
|
||||||
#include <wrl.h>
|
#include <wrl.h>
|
||||||
#include "result.h"
|
#include "result.h"
|
||||||
#include "common.h" // wistd type_traits helpers
|
#include "common.h" // wistd type_traits helpers
|
||||||
|
#include <libloaderapi.h> // GetModuleHandleW
|
||||||
|
|
||||||
|
/// @cond
|
||||||
|
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||||||
|
/// @endcond
|
||||||
|
|
||||||
namespace wil
|
namespace wil
|
||||||
{
|
{
|
||||||
|
@ -79,6 +84,44 @@ namespace wil
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
#endif // WIL_ENABLE_EXCEPTIONS
|
#endif // WIL_ENABLE_EXCEPTIONS
|
||||||
|
|
||||||
|
/** Holds a reference to the host WRL module to prevent it from being unloaded.
|
||||||
|
Normally, the reference is held implicitly because you are a member function
|
||||||
|
of a DLL-hosted COM object, or because you retain a strong reference
|
||||||
|
to some DLL-hosted COM object, but if those do not apply to you, then you
|
||||||
|
will need to hold a reference explicitly. For examples (and for the C++/WinRT
|
||||||
|
equivalent), see winrt_module_reference.
|
||||||
|
*/
|
||||||
|
struct [[nodiscard]] wrl_module_reference
|
||||||
|
{
|
||||||
|
wrl_module_reference()
|
||||||
|
{
|
||||||
|
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
|
||||||
|
{
|
||||||
|
modulePtr->IncrementObjectCount();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef GET_MODULE_HANDLE_EX_FLAG_PIN
|
||||||
|
// If this assertion fails, then you are using wrl_module_reference
|
||||||
|
// from a DLL that does not host WRL objects, and the module reference
|
||||||
|
// has no effect.
|
||||||
|
WI_ASSERT(reinterpret_cast<HMODULE>(&__ImageBase) == GetModuleHandleW(nullptr));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wrl_module_reference(wrl_module_reference const&) : wrl_module_reference() {}
|
||||||
|
|
||||||
|
~wrl_module_reference()
|
||||||
|
{
|
||||||
|
if (auto modulePtr = ::Microsoft::WRL::GetModuleBase())
|
||||||
|
{
|
||||||
|
modulePtr->DecrementObjectCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace wil
|
} // namespace wil
|
||||||
|
|
||||||
#endif // __WIL_WRL_INCLUDED
|
#endif // __WIL_WRL_INCLUDED
|
||||||
|
|
|
@ -0,0 +1,114 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
<Type Name="wistd::_Func_impl<*>">
|
||||||
|
<DisplayString>{_Callee._Object}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="wistd::function<*>">
|
||||||
|
<DisplayString Condition="_Impl == 0">empty</DisplayString>
|
||||||
|
<DisplayString Condition="_Impl != 0">{*_Impl}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="_Impl != 0">*_Impl</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="wistd::unique_ptr<*>">
|
||||||
|
<DisplayString Condition="_Myptr == 0">empty</DisplayString>
|
||||||
|
<DisplayString Condition="_Myptr != 0">{*_Myptr}</DisplayString>
|
||||||
|
<StringView>_Myptr</StringView>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[pointer]" Condition="_Myptr != 0">_Myptr</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="wistd::unique_ptr<wchar_t[*],*>">
|
||||||
|
<DisplayString Condition="_Myptr == 0">empty</DisplayString>
|
||||||
|
<DisplayString Condition="_Myptr != 0">{_Myptr,su}</DisplayString>
|
||||||
|
<StringView>_Myptr</StringView>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[pointer]" Condition="_Myptr != 0">_Myptr</Item>
|
||||||
|
<Item Name="[length]" Condition="_Myptr != 0">wcslen(_Myptr)</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="wistd::unique_ptr<char[*],*>">
|
||||||
|
<DisplayString Condition="_Myptr == 0">empty</DisplayString>
|
||||||
|
<DisplayString Condition="_Myptr != 0">{_Myptr,s}</DisplayString>
|
||||||
|
<StringView>_Myptr</StringView>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[pointer]" Condition="_Myptr != 0">_Myptr</Item>
|
||||||
|
<Item Name="[length]" Condition="_Myptr != 0">strlen(_Myptr)</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="wil::details::shared_storage<*>">
|
||||||
|
<DisplayString Condition="m_ptr == 0">empty</DisplayString>
|
||||||
|
<DisplayString Condition="m_ptr != 0">{*m_ptr}</DisplayString>
|
||||||
|
<StringView>m_ptr</StringView>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[pointer]" Condition="m_ptr != 0">m_ptr</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="wil::details::unique_storage<wil::details::handle_null_resource_policy<*>>">
|
||||||
|
<DisplayString Condition="m_ptr == 0">empty</DisplayString>
|
||||||
|
<DisplayString Condition="m_ptr != 0">{m_ptr}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="wil::details::unique_storage<wil::details::handle_invalid_resource_policy<*>>">
|
||||||
|
<DisplayString Condition="m_ptr == 0">empty</DisplayString>
|
||||||
|
<DisplayString Condition="m_ptr != 0">{m_ptr}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="wil::details::unique_storage<wil::details::resource_policy<*>>">
|
||||||
|
<DisplayString Condition="m_ptr == 0">empty</DisplayString>
|
||||||
|
<DisplayString Condition="m_ptr != 0">{m_ptr}</DisplayString>
|
||||||
|
<StringView>m_ptr</StringView>
|
||||||
|
<Expand>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="wil::details::unique_storage<wil::details::resource_policy<wchar_t *,*>>">
|
||||||
|
<DisplayString Condition="m_ptr == 0">empty</DisplayString>
|
||||||
|
<DisplayString Condition="m_ptr != 0">{m_ptr,su}</DisplayString>
|
||||||
|
<StringView>m_ptr</StringView>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[length]" Condition="m_ptr != 0">wcslen(m_ptr)</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="wil::details::unique_storage<wil::details::resource_policy<char *,*>>">
|
||||||
|
<DisplayString Condition="m_ptr == 0">empty</DisplayString>
|
||||||
|
<DisplayString Condition="m_ptr != 0">{m_ptr,s}</DisplayString>
|
||||||
|
<StringView>m_ptr</StringView>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[length]" Condition="m_ptr != 0">strlen(m_ptr)</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="wil::com_ptr_t<*>">
|
||||||
|
<DisplayString>{m_ptr}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>m_ptr</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
|
||||||
|
<Type Name="wil::basic_zstring_view<*>">
|
||||||
|
<Intrinsic Name="size" Expression="_Mysize" />
|
||||||
|
<Intrinsic Name="data" Expression="_Mydata" />
|
||||||
|
<DisplayString>{_Mydata,[_Mysize]}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[size]" ExcludeView="simple">size()</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
</AutoVisualizer>
|
|
@ -7,6 +7,7 @@ file(TO_NATIVE_PATH "${nupkg_dir}/Microsoft.Windows.ImplementationLibrary.${WIL_
|
||||||
file(DOWNLOAD https://dist.nuget.org/win-x86-commandline/latest/nuget.exe ${nuget_exe})
|
file(DOWNLOAD https://dist.nuget.org/win-x86-commandline/latest/nuget.exe ${nuget_exe})
|
||||||
|
|
||||||
file(GLOB_RECURSE wil_headers ${CMAKE_SOURCE_DIR}/include/*.h)
|
file(GLOB_RECURSE wil_headers ${CMAKE_SOURCE_DIR}/include/*.h)
|
||||||
|
file(GLOB_RECURSE wil_natvis ${CMAKE_SOURCE_DIR}/natvis/*.natvis)
|
||||||
|
|
||||||
add_custom_command(OUTPUT ${wil_nupkg}
|
add_custom_command(OUTPUT ${wil_nupkg}
|
||||||
COMMAND ${nuget_exe} pack ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec -OutputDirectory ${nupkg_dir} -Version ${WIL_BUILD_VERSION} -NonInteractive
|
COMMAND ${nuget_exe} pack ${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec -OutputDirectory ${nupkg_dir} -Version ${WIL_BUILD_VERSION} -NonInteractive
|
||||||
|
@ -14,6 +15,7 @@ add_custom_command(OUTPUT ${wil_nupkg}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec
|
${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.targets
|
${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.targets
|
||||||
${wil_headers}
|
${wil_headers}
|
||||||
|
${wil_natvis}
|
||||||
${CMAKE_SOURCE_DIR}/LICENSE
|
${CMAKE_SOURCE_DIR}/LICENSE
|
||||||
${CMAKE_SOURCE_DIR}/ThirdPartyNotices.txt)
|
${CMAKE_SOURCE_DIR}/ThirdPartyNotices.txt)
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
<file src="..\..\LICENSE"/>
|
<file src="..\..\LICENSE"/>
|
||||||
<file src="..\..\ThirdPartyNotices.txt"/>
|
<file src="..\..\ThirdPartyNotices.txt"/>
|
||||||
<file src="..\..\include\**" target="include\" />
|
<file src="..\..\include\**" target="include\" />
|
||||||
|
<file src="..\..\natvis\wil.natvis" target="natvis\" />
|
||||||
<file src="Microsoft.Windows.ImplementationLibrary.targets" target="build\native\" />
|
<file src="Microsoft.Windows.ImplementationLibrary.targets" target="build\native\" />
|
||||||
</files>
|
</files>
|
||||||
</package>
|
</package>
|
|
@ -5,4 +5,7 @@
|
||||||
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis"/>
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -8,7 +8,7 @@ jobs:
|
||||||
timeoutInMinutes: 360
|
timeoutInMinutes: 360
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'windows-2019'
|
vmImage: 'windows-2022'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- script: |
|
- script: |
|
||||||
|
@ -18,7 +18,7 @@ jobs:
|
||||||
displayName: 'Install Clang'
|
displayName: 'Install Clang'
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars32.bat"
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsamd64_x86.bat"
|
||||||
if %ERRORLEVEL% NEQ 0 goto :eof
|
if %ERRORLEVEL% NEQ 0 goto :eof
|
||||||
|
|
||||||
call scripts\init_all.cmd --fast
|
call scripts\init_all.cmd --fast
|
||||||
|
@ -28,7 +28,7 @@ jobs:
|
||||||
displayName: 'Build x86'
|
displayName: 'Build x86'
|
||||||
|
|
||||||
- script: |
|
- script: |
|
||||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||||
if %ERRORLEVEL% NEQ 0 goto :eof
|
if %ERRORLEVEL% NEQ 0 goto :eof
|
||||||
|
|
||||||
call scripts\init_all.cmd --fast
|
call scripts\init_all.cmd --fast
|
||||||
|
@ -37,5 +37,9 @@ jobs:
|
||||||
call scripts\build_all.cmd
|
call scripts\build_all.cmd
|
||||||
displayName: 'Build x64'
|
displayName: 'Build x64'
|
||||||
|
|
||||||
- script: call scripts\runtests.cmd
|
# NOTE: We run the tests in the 32-bit cross-tools window out of convenience as this adds all necessary directories to
|
||||||
|
# the PATH that are necessary for finding the ASan/UBSan DLLs
|
||||||
|
- script: |
|
||||||
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsamd64_x86.bat""
|
||||||
|
call scripts\runtests.cmd ~[LocalOnly]
|
||||||
displayName: 'Run Tests'
|
displayName: 'Run Tests'
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
@echo off
|
@echo off
|
||||||
|
setlocal
|
||||||
setlocal EnableDelayedExpansion
|
setlocal EnableDelayedExpansion
|
||||||
|
|
||||||
set BUILD_ROOT=%~dp0\..\build
|
set BUILD_ROOT=%~dp0\..\build
|
||||||
|
@ -15,23 +16,15 @@ if "%Platform%"=="x64" (
|
||||||
exit /B 1
|
exit /B 1
|
||||||
)
|
)
|
||||||
|
|
||||||
call :build clang debug
|
set COMPILERS=clang msvc
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
set BUILD_TYPES=debug release relwithdebinfo minsizerel
|
||||||
call :build clang release
|
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
call :build clang relwithdebinfo
|
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
call :build clang minsizerel
|
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
|
|
||||||
call :build msvc debug
|
for %%c in (%COMPILERS%) do (
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
for %%b in (%BUILD_TYPES%) do (
|
||||||
call :build msvc release
|
call :build %%c %%b
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
if !ERRORLEVEL! NEQ 0 ( goto :eof )
|
||||||
call :build msvc relwithdebinfo
|
)
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
)
|
||||||
call :build msvc minsizerel
|
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
|
|
||||||
echo All build completed successfully!
|
echo All build completed successfully!
|
||||||
|
|
||||||
|
@ -47,5 +40,6 @@ if not exist %BUILD_DIR% (
|
||||||
pushd %BUILD_DIR%
|
pushd %BUILD_DIR%
|
||||||
echo Building from %CD%
|
echo Building from %CD%
|
||||||
ninja
|
ninja
|
||||||
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
popd
|
popd
|
||||||
goto :eof
|
exit /B %EXIT_CODE%
|
||||||
|
|
|
@ -10,8 +10,8 @@ goto :init
|
||||||
:usage
|
:usage
|
||||||
echo USAGE:
|
echo USAGE:
|
||||||
echo init.cmd [--help] [-c^|--compiler ^<clang^|msvc^>] [-g^|--generator ^<ninja^|msbuild^>]
|
echo init.cmd [--help] [-c^|--compiler ^<clang^|msvc^>] [-g^|--generator ^<ninja^|msbuild^>]
|
||||||
echo [-b^|--build-type ^<debug^|release^|relwithdebinfo^|minsizerel^>] [-l^|--linker link^|lld-link]
|
echo [-b^|--build-type ^<debug^|release^|relwithdebinfo^|minsizerel^>] [-v^|--version X.Y.Z]
|
||||||
echo [--fast] [-v^|--version X.Y.Z]
|
echo [--cppwinrt ^<version^>] [--fast]
|
||||||
echo.
|
echo.
|
||||||
echo ARGUMENTS
|
echo ARGUMENTS
|
||||||
echo -c^|--compiler Controls the compiler used, either 'clang' (the default) or 'msvc'
|
echo -c^|--compiler Controls the compiler used, either 'clang' (the default) or 'msvc'
|
||||||
|
@ -20,6 +20,7 @@ goto :init
|
||||||
echo 'relwithdebinfo', or 'minsizerel'
|
echo 'relwithdebinfo', or 'minsizerel'
|
||||||
echo -v^|--version Specifies the version of the NuGet package produced. Primarily only used by the CI
|
echo -v^|--version Specifies the version of the NuGet package produced. Primarily only used by the CI
|
||||||
echo build and is typically not necessary when building locally
|
echo build and is typically not necessary when building locally
|
||||||
|
echo --cppwinrt Manually specifies the version of C++/WinRT to use for generating headers
|
||||||
echo --fast Used to (slightly) reduce compile times and build output size. This is primarily
|
echo --fast Used to (slightly) reduce compile times and build output size. This is primarily
|
||||||
echo used by the CI build machines where resources are more constrained. This switch is
|
echo used by the CI build machines where resources are more constrained. This switch is
|
||||||
echo temporary and will be removed once https://github.com/microsoft/wil/issues/9 is fixed
|
echo temporary and will be removed once https://github.com/microsoft/wil/issues/9 is fixed
|
||||||
|
@ -30,10 +31,10 @@ goto :init
|
||||||
set COMPILER=
|
set COMPILER=
|
||||||
set GENERATOR=
|
set GENERATOR=
|
||||||
set BUILD_TYPE=
|
set BUILD_TYPE=
|
||||||
set LINKER=
|
|
||||||
set CMAKE_ARGS=
|
set CMAKE_ARGS=
|
||||||
set BITNESS=
|
set BITNESS=
|
||||||
set VERSION=
|
set VERSION=
|
||||||
|
set CPPWINRT_VERSION=
|
||||||
set FAST_BUILD=0
|
set FAST_BUILD=0
|
||||||
|
|
||||||
:parse
|
:parse
|
||||||
|
@ -88,29 +89,25 @@ goto :init
|
||||||
goto :parse
|
goto :parse
|
||||||
)
|
)
|
||||||
|
|
||||||
set LINKER_SET=0
|
set VERSION_SET=0
|
||||||
if /I "%~1"=="-l" set LINKER_SET=1
|
if /I "%~1"=="-v" set VERSION_SET=1
|
||||||
if /I "%~1"=="--linker" set LINKER_SET=1
|
if /I "%~1"=="--version" set VERSION_SET=1
|
||||||
if %LINKER_SET%==1 (
|
if %VERSION_SET%==1 (
|
||||||
if "%LINKER%" NEQ "" echo ERROR: Linker already specified & call :usage & exit /B 1
|
if "%VERSION%" NEQ "" echo ERROR: Version already specified & call :usage & exit /B 1
|
||||||
|
if /I "%~2"=="" echo ERROR: Version string missing & call :usage & exit /B 1
|
||||||
|
|
||||||
if /I "%~2"=="link" set LINKER=link
|
set VERSION=%~2
|
||||||
if /I "%~2"=="lld-link" set LINKER=lld-link
|
|
||||||
if "!LINKER!"=="" echo ERROR: Unrecognized/missing linker %~2 & call :usage & exit /B 1
|
|
||||||
|
|
||||||
shift
|
shift
|
||||||
shift
|
shift
|
||||||
goto :parse
|
goto :parse
|
||||||
)
|
)
|
||||||
|
|
||||||
set VERSION_SET=0
|
if /I "%~1"=="--cppwinrt" (
|
||||||
if /I "%~1"=="-v" set VERSION_SET=1
|
if "%CPPWINRT_VERSION%" NEQ "" echo ERROR: C++/WinRT version already specified & call :usage & exit /B 1
|
||||||
if /I "%~1"=="--version" set VERSION_SET=1
|
if /I "%~2"=="" echo ERROR: C++/WinRT version string missing & call :usage & exit /B 1
|
||||||
if %VERSION_SET%==1 (
|
|
||||||
if "%VERSION%" NEQ "" echo ERROR: Version alread specified & call :usage & exit /B 1
|
|
||||||
if /I "%~2"=="" echo ERROR: Version string missing & call :usage & exit /B 1
|
|
||||||
|
|
||||||
set VERSION=%~2
|
set CPPWINRT_VERSION=%~2
|
||||||
|
|
||||||
shift
|
shift
|
||||||
shift
|
shift
|
||||||
|
@ -132,9 +129,6 @@ goto :init
|
||||||
:: Check for conflicting arguments
|
:: Check for conflicting arguments
|
||||||
if "%GENERATOR%"=="msbuild" (
|
if "%GENERATOR%"=="msbuild" (
|
||||||
if "%COMPILER%"=="clang" echo ERROR: Cannot use Clang with MSBuild & exit /B 1
|
if "%COMPILER%"=="clang" echo ERROR: Cannot use Clang with MSBuild & exit /B 1
|
||||||
|
|
||||||
:: While CMake won't give an error, specifying the linker won't actually have any effect with the VS generator
|
|
||||||
if "%LINKER%"=="lld-link" echo ERROR: Cannot use lld-link with MSBuild & exit /B 1
|
|
||||||
)
|
)
|
||||||
|
|
||||||
:: Select defaults
|
:: Select defaults
|
||||||
|
@ -145,8 +139,6 @@ goto :init
|
||||||
|
|
||||||
if "%BUILD_TYPE%"=="" set BUILD_TYPE=debug
|
if "%BUILD_TYPE%"=="" set BUILD_TYPE=debug
|
||||||
|
|
||||||
if "%LINKER%"=="" set LINKER=link
|
|
||||||
|
|
||||||
:: Formulate CMake arguments
|
:: Formulate CMake arguments
|
||||||
if %GENERATOR%==ninja set CMAKE_ARGS=%CMAKE_ARGS% -G Ninja
|
if %GENERATOR%==ninja set CMAKE_ARGS=%CMAKE_ARGS% -G Ninja
|
||||||
|
|
||||||
|
@ -167,14 +159,14 @@ goto :init
|
||||||
set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_SYSTEM_VERSION=10.0
|
set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_SYSTEM_VERSION=10.0
|
||||||
)
|
)
|
||||||
|
|
||||||
if %LINKER%==lld-link (
|
|
||||||
set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_LINKER=lld-link
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%VERSION%" NEQ "" set CMAKE_ARGS=%CMAKE_ARGS% -DWIL_BUILD_VERSION=%VERSION%
|
if "%VERSION%" NEQ "" set CMAKE_ARGS=%CMAKE_ARGS% -DWIL_BUILD_VERSION=%VERSION%
|
||||||
|
|
||||||
|
if "%CPPWINRT_VERSION%" NEQ "" set CMAKE_ARGS=%CMAKE_ARGS% -DCPPWINRT_VERSION=%CPPWINRT_VERSION%
|
||||||
|
|
||||||
if %FAST_BUILD%==1 set CMAKE_ARGS=%CMAKE_ARGS% -DFAST_BUILD=ON
|
if %FAST_BUILD%==1 set CMAKE_ARGS=%CMAKE_ARGS% -DFAST_BUILD=ON
|
||||||
|
|
||||||
|
set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||||
|
|
||||||
:: Figure out the platform
|
:: Figure out the platform
|
||||||
if "%Platform%"=="" echo ERROR: The init.cmd script must be run from a Visual Studio command window & exit /B 1
|
if "%Platform%"=="" echo ERROR: The init.cmd script must be run from a Visual Studio command window & exit /B 1
|
||||||
if "%Platform%"=="x86" (
|
if "%Platform%"=="x86" (
|
||||||
|
@ -191,7 +183,6 @@ goto :init
|
||||||
:: Run CMake
|
:: Run CMake
|
||||||
pushd %BUILD_DIR%
|
pushd %BUILD_DIR%
|
||||||
echo Using compiler....... %COMPILER%
|
echo Using compiler....... %COMPILER%
|
||||||
echo Using linker......... %LINKER%
|
|
||||||
echo Using architecture... %Platform%
|
echo Using architecture... %Platform%
|
||||||
echo Using build type..... %BUILD_TYPE%
|
echo Using build type..... %BUILD_TYPE%
|
||||||
echo Using build root..... %CD%
|
echo Using build root..... %CD%
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
@echo off
|
@echo off
|
||||||
|
setlocal
|
||||||
|
setlocal EnableDelayedExpansion
|
||||||
|
|
||||||
:: NOTE: Architecture is picked up from the command window, so we can't control that here :(
|
:: NOTE: Architecture is picked up from the command window, so we can't control that here :(
|
||||||
|
set COMPILERS=clang msvc
|
||||||
|
set BUILD_TYPES=debug relwithdebinfo
|
||||||
|
|
||||||
call %~dp0\init.cmd -c clang -g ninja -b debug %*
|
for %%c in (%COMPILERS%) do (
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
for %%b in (%BUILD_TYPES%) do (
|
||||||
call %~dp0\init.cmd -c clang -g ninja -b relwithdebinfo %*
|
call %~dp0\init.cmd -c %%c -g ninja -b %%b %*
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
if !ERRORLEVEL! NEQ 0 ( goto :eof )
|
||||||
|
)
|
||||||
call %~dp0\init.cmd -c msvc -g ninja -b debug %*
|
)
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
call %~dp0\init.cmd -c msvc -g ninja -b relwithdebinfo %*
|
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
|
|
|
@ -1,44 +1,24 @@
|
||||||
@echo off
|
@echo off
|
||||||
|
setlocal
|
||||||
setlocal EnableDelayedExpansion
|
setlocal EnableDelayedExpansion
|
||||||
|
|
||||||
|
set TEST_ARGS=%*
|
||||||
|
|
||||||
set BUILD_ROOT=%~dp0\..\build
|
set BUILD_ROOT=%~dp0\..\build
|
||||||
|
|
||||||
:: Unlike building, we don't need to limit ourselves to the Platform of the command window
|
:: Unlike building, we don't need to limit ourselves to the Platform of the command window
|
||||||
call :execute_tests clang64debug
|
set COMPILERS=clang msvc
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
set ARCHITECTURES=32 64
|
||||||
call :execute_tests clang64release
|
set BUILD_TYPES=debug release relwithdebinfo minsizerel
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
call :execute_tests clang64relwithdebinfo
|
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
call :execute_tests clang64minsizerel
|
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
|
|
||||||
call :execute_tests clang32debug
|
for %%c in (%COMPILERS%) do (
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
for %%a in (%ARCHITECTURES%) do (
|
||||||
call :execute_tests clang32release
|
for %%b in (%BUILD_TYPES%) do (
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
call :execute_tests %%c%%a%%b
|
||||||
call :execute_tests clang32relwithdebinfo
|
if !ERRORLEVEL! NEQ 0 ( goto :eof )
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
)
|
||||||
call :execute_tests clang32minsizerel
|
)
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
)
|
||||||
|
|
||||||
call :execute_tests msvc64debug
|
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
call :execute_tests msvc64release
|
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
call :execute_tests msvc64relwithdebinfo
|
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
call :execute_tests msvc64minsizerel
|
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
|
|
||||||
call :execute_tests msvc32debug
|
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
call :execute_tests msvc32release
|
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
call :execute_tests msvc32relwithdebinfo
|
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
call :execute_tests msvc32minsizerel
|
|
||||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
|
||||||
|
|
||||||
goto :eof
|
goto :eof
|
||||||
|
|
||||||
|
@ -49,19 +29,27 @@ if not exist %BUILD_DIR% ( goto :eof )
|
||||||
pushd %BUILD_DIR%
|
pushd %BUILD_DIR%
|
||||||
echo Running tests from %CD%
|
echo Running tests from %CD%
|
||||||
call :execute_test app witest.app.exe
|
call :execute_test app witest.app.exe
|
||||||
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
|
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
|
||||||
call :execute_test cpplatest witest.cpplatest.exe
|
call :execute_test cpplatest witest.cpplatest.exe
|
||||||
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
|
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
|
||||||
call :execute_test noexcept witest.noexcept.exe
|
call :execute_test noexcept witest.noexcept.exe
|
||||||
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
|
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
|
||||||
call :execute_test normal witest.exe
|
call :execute_test normal witest.exe
|
||||||
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
|
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
|
||||||
popd
|
call :execute_test sanitize-address witest.asan.exe
|
||||||
|
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
|
||||||
|
call :execute_test sanitize-undefined-behavior witest.ubsan.exe
|
||||||
|
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
|
||||||
|
call :execute_test win7 witest.win7.exe
|
||||||
|
if %ERRORLEVEL% NEQ 0 ( goto :execute_tests_done )
|
||||||
|
|
||||||
goto :eof
|
:execute_tests_done
|
||||||
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
|
popd
|
||||||
|
exit /B %EXIT_CODE%
|
||||||
|
|
||||||
:execute_test
|
:execute_test
|
||||||
if not exist tests\%1\%2 ( goto :eof )
|
if not exist tests\%1\%2 ( goto :eof )
|
||||||
echo Running %1 tests...
|
echo Running %1 tests...
|
||||||
tests\%1\%2
|
tests\%1\%2 %TEST_ARGS%
|
||||||
goto :eof
|
goto :eof
|
||||||
|
|
|
@ -1,19 +1,98 @@
|
||||||
|
|
||||||
include(${CMAKE_SOURCE_DIR}/cmake/common_build_flags.cmake)
|
include(${PROJECT_SOURCE_DIR}/cmake/common_build_flags.cmake)
|
||||||
|
|
||||||
# All projects need to reference the WIL headers
|
# All projects need to reference the WIL headers
|
||||||
include_directories(${CMAKE_SOURCE_DIR}/include)
|
include_directories(${PROJECT_SOURCE_DIR}/include)
|
||||||
|
|
||||||
# TODO: Might be worth trying to conditionally do this on SDK version, assuming there's a semi-easy way to detect that
|
# TODO: Might be worth trying to conditionally do this on SDK version, assuming there's a semi-easy way to detect that
|
||||||
include_directories(BEFORE SYSTEM ./workarounds/wrl)
|
include_directories(BEFORE SYSTEM ./workarounds/wrl)
|
||||||
|
|
||||||
|
# Because we don't always use msbuild, we need to run nuget manually
|
||||||
|
find_program(NUGET nuget)
|
||||||
|
if (NOT NUGET)
|
||||||
|
message(FATAL_ERROR "Unable to find the nuget CLI tool. Please install it from https://www.nuget.org/downloads and ensure it has been added to the PATH")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
execute_process(COMMAND
|
||||||
|
${NUGET} install Microsoft.Windows.CppWinRT -Version ${CPPWINRT_VERSION} -OutputDirectory packages
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
|
RESULT_VARIABLE ret)
|
||||||
|
if (NOT ret EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Failed to install nuget package Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(CPPWINRT ${CMAKE_BINARY_DIR}/packages/Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}/bin/cppwinrt.exe)
|
||||||
|
execute_process(COMMAND
|
||||||
|
${CPPWINRT} -input sdk -output include
|
||||||
|
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||||
|
RESULT_VARIABLE ret)
|
||||||
|
if (NOT ret EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Failed to run cppwinrt.exe")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include_directories(BEFORE SYSTEM ${CMAKE_BINARY_DIR}/include)
|
||||||
|
|
||||||
# The build pipelines have limitations that local development environments do not, so turn a few knobs
|
# The build pipelines have limitations that local development environments do not, so turn a few knobs
|
||||||
if (${FAST_BUILD})
|
if (${FAST_BUILD})
|
||||||
replace_cxx_flag("/GR" "/GR-") # Disables RTTI
|
replace_cxx_flag("/GR" "/GR-") # Disables RTTI
|
||||||
add_definitions(-DCATCH_CONFIG_FAST_COMPILE -DWIL_FAST_BUILD)
|
add_definitions(-DCATCH_CONFIG_FAST_COMPILE -DWIL_FAST_BUILD)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# For some unknown reason, 'RelWithDebInfo' compiles with '/Ob1' as opposed to '/Ob2' which prevents inlining of
|
||||||
|
# functions not marked 'inline'. The reason we prefer 'RelWithDebInfo' over 'Release' is to get debug info, so manually
|
||||||
|
# revert to the desired (and default) inlining behavior as that exercises more interesting code paths
|
||||||
|
if (${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo")
|
||||||
|
# TODO: This is currently blocked by an apparent Clang bug: https://github.com/llvm/llvm-project/issues/59690
|
||||||
|
# replace_cxx_flag("/Ob1" "/Ob2")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(COMMON_SOURCES
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/main.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/CommonTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/ComTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/FileSystemTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/NTResultTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/ResourceTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/ResultTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/Rpc.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/SafeCastTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/TraceLoggingTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/WistdTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/wiTest.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../natvis/wil.natvis
|
||||||
|
)
|
||||||
|
|
||||||
add_subdirectory(app)
|
add_subdirectory(app)
|
||||||
add_subdirectory(cpplatest)
|
add_subdirectory(cpplatest)
|
||||||
add_subdirectory(noexcept)
|
add_subdirectory(noexcept)
|
||||||
add_subdirectory(normal)
|
add_subdirectory(normal)
|
||||||
|
add_subdirectory(win7)
|
||||||
|
|
||||||
|
set(DEBUG_BUILD FALSE)
|
||||||
|
set(HAS_DEBUG_INFO FALSE)
|
||||||
|
|
||||||
|
if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")
|
||||||
|
set(DEBUG_BUILD TRUE)
|
||||||
|
set(HAS_DEBUG_INFO TRUE)
|
||||||
|
elseif(${CMAKE_BUILD_TYPE} STREQUAL "RelWithDebInfo")
|
||||||
|
set(HAS_DEBUG_INFO TRUE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(ASAN_AVAILABLE FALSE)
|
||||||
|
set(UBSAN_AVAILABLE FALSE)
|
||||||
|
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||||
|
# Address Sanitizer is available for all architectures and build types, but warns/errors if debug info is not enabled
|
||||||
|
set(ASAN_AVAILABLE ${HAS_DEBUG_INFO})
|
||||||
|
elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
# Address Sanitizer is not available with debug libraries
|
||||||
|
set(ASAN_AVAILABLE NOT ${DEBUG_BUILD})
|
||||||
|
set(UBSAN_AVAILABLE NOT ${DEBUG_BUILD})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (${ASAN_AVAILABLE})
|
||||||
|
add_subdirectory(sanitize-address)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (${UBSAN_AVAILABLE})
|
||||||
|
add_subdirectory(sanitize-undefined-behavior)
|
||||||
|
endif()
|
||||||
|
|
|
@ -0,0 +1,401 @@
|
||||||
|
#include <wil/com_apartment_variable.h>
|
||||||
|
#include <wil/com.h>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
|
||||||
|
|
||||||
|
template <typename... args_t>
|
||||||
|
inline void LogOutput(_Printf_format_string_ PCWSTR format, args_t&&... args)
|
||||||
|
{
|
||||||
|
OutputDebugStringW(wil::str_printf_failfast<wil::unique_cotaskmem_string>(format, wistd::forward<args_t>(args)...).get());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool IsComInitialized()
|
||||||
|
{
|
||||||
|
APTTYPE type{}; APTTYPEQUALIFIER qualifier{};
|
||||||
|
return CoGetApartmentType(&type, &qualifier) == S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void WaitForAllComApartmentsToRundown()
|
||||||
|
{
|
||||||
|
while (IsComInitialized())
|
||||||
|
{
|
||||||
|
Sleep(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void co_wait(const wil::unique_event& e)
|
||||||
|
{
|
||||||
|
HANDLE raw[] = { e.get() };
|
||||||
|
ULONG index{};
|
||||||
|
REQUIRE_SUCCEEDED(CoWaitForMultipleHandles(COWAIT_DISPATCH_CALLS, INFINITE, static_cast<ULONG>(std::size(raw)), raw, &index));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunApartmentVariableTest(void(*test)())
|
||||||
|
{
|
||||||
|
test();
|
||||||
|
// Apartment variable rundown is async, wait for the last COM apartment
|
||||||
|
// to rundown before proceeding to the next test.
|
||||||
|
WaitForAllComApartmentsToRundown();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mock_platform
|
||||||
|
{
|
||||||
|
static unsigned long long GetApartmentId()
|
||||||
|
{
|
||||||
|
APTTYPE type; APTTYPEQUALIFIER qualifer;
|
||||||
|
REQUIRE_SUCCEEDED(CoGetApartmentType(&type, &qualifer)); // ensure COM is inited
|
||||||
|
|
||||||
|
// Approximate apartment Id
|
||||||
|
if (type == APTTYPE_STA)
|
||||||
|
{
|
||||||
|
REQUIRE_FALSE(GetCurrentThreadId() < APTTYPE_MAINSTA);
|
||||||
|
return GetCurrentThreadId();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// APTTYPE_MTA (1), APTTYPE_NA (2), APTTYPE_MAINSTA (3)
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto RegisterForApartmentShutdown(IApartmentShutdown* observer)
|
||||||
|
{
|
||||||
|
const auto id = GetApartmentId();
|
||||||
|
auto apt_observers = m_observers.find(id);
|
||||||
|
if (apt_observers == m_observers.end())
|
||||||
|
{
|
||||||
|
m_observers.insert({ id, { observer} });
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
apt_observers->second.emplace_back(observer);
|
||||||
|
}
|
||||||
|
return shutdown_type{ reinterpret_cast<APARTMENT_SHUTDOWN_REGISTRATION_COOKIE>(id) };
|
||||||
|
}
|
||||||
|
|
||||||
|
static void UnRegisterForApartmentShutdown(APARTMENT_SHUTDOWN_REGISTRATION_COOKIE cookie)
|
||||||
|
{
|
||||||
|
auto id = reinterpret_cast<unsigned long long>(cookie);
|
||||||
|
m_observers.erase(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
using shutdown_type = wil::unique_any<APARTMENT_SHUTDOWN_REGISTRATION_COOKIE, decltype(&UnRegisterForApartmentShutdown), UnRegisterForApartmentShutdown>;
|
||||||
|
|
||||||
|
// This is needed to simulate the platform for unit testing.
|
||||||
|
static auto CoInitializeEx(DWORD coinitFlags = 0 /*COINIT_MULTITHREADED*/)
|
||||||
|
{
|
||||||
|
return wil::scope_exit([aptId = GetCurrentThreadId(), init = wil::CoInitializeEx(coinitFlags)]()
|
||||||
|
{
|
||||||
|
const auto id = GetApartmentId();
|
||||||
|
auto apt_observers = m_observers.find(id);
|
||||||
|
if (apt_observers != m_observers.end())
|
||||||
|
{
|
||||||
|
const auto& observers = apt_observers->second;
|
||||||
|
for (auto& observer : observers)
|
||||||
|
{
|
||||||
|
observer->OnUninitialize(id);
|
||||||
|
}
|
||||||
|
m_observers.erase(apt_observers);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable the test hook to force losing the race
|
||||||
|
inline static constexpr unsigned long AsyncRundownDelayForTestingRaces = 1; // enable test hook
|
||||||
|
inline static std::unordered_map<unsigned long long, std::vector<wil::com_ptr<IApartmentShutdown>>> m_observers;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto fn() { return 42; };
|
||||||
|
auto fn2() { return 43; };
|
||||||
|
|
||||||
|
wil::apartment_variable<int, wil::apartment_variable_leak_action::ignore, mock_platform> g_v1;
|
||||||
|
wil::apartment_variable<int, wil::apartment_variable_leak_action::ignore> g_v2;
|
||||||
|
|
||||||
|
template <typename platform = wil::apartment_variable_platform>
|
||||||
|
void TestApartmentVariableAllMethods()
|
||||||
|
{
|
||||||
|
auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED);
|
||||||
|
|
||||||
|
std::ignore = g_v1.get_or_create(fn);
|
||||||
|
|
||||||
|
wil::apartment_variable<int, wil::apartment_variable_leak_action::fail_fast, platform> v1;
|
||||||
|
|
||||||
|
REQUIRE(v1.get_if() == nullptr);
|
||||||
|
REQUIRE(v1.get_or_create(fn) == 42);
|
||||||
|
int value = 43;
|
||||||
|
v1.set(value);
|
||||||
|
REQUIRE(v1.get_or_create(fn) == 43);
|
||||||
|
REQUIRE(v1.get_existing() == 43);
|
||||||
|
v1.clear();
|
||||||
|
REQUIRE(v1.get_if() == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename platform = wil::apartment_variable_platform>
|
||||||
|
void TestApartmentVariableGetOrCreateForms()
|
||||||
|
{
|
||||||
|
auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED);
|
||||||
|
|
||||||
|
wil::apartment_variable<int, wil::apartment_variable_leak_action::fail_fast, platform> v1;
|
||||||
|
REQUIRE(v1.get_or_create(fn) == 42);
|
||||||
|
v1.clear();
|
||||||
|
REQUIRE(v1.get_or_create([&]
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}) == 1);
|
||||||
|
v1.clear();
|
||||||
|
REQUIRE(v1.get_or_create() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename platform = wil::apartment_variable_platform>
|
||||||
|
void TestApartmentVariableLifetimes()
|
||||||
|
{
|
||||||
|
wil::apartment_variable<int, wil::apartment_variable_leak_action::fail_fast, platform> av1, av2;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED);
|
||||||
|
|
||||||
|
auto v1 = av1.get_or_create(fn);
|
||||||
|
REQUIRE(av1.storage().size() == 1);
|
||||||
|
auto v2 = av1.get_existing();
|
||||||
|
REQUIRE(av1.current_apartment_variable_count() == 1);
|
||||||
|
REQUIRE(v1 == v2);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED);
|
||||||
|
auto v1 = av1.get_or_create(fn);
|
||||||
|
auto v2 = av2.get_or_create(fn2);
|
||||||
|
REQUIRE((av1.current_apartment_variable_count() == 2));
|
||||||
|
REQUIRE(v1 != v2);
|
||||||
|
REQUIRE(av1.storage().size() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(av1.storage().size() == 0);
|
||||||
|
|
||||||
|
{
|
||||||
|
auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED);
|
||||||
|
|
||||||
|
auto v = av1.get_or_create(fn);
|
||||||
|
REQUIRE(av1.current_apartment_variable_count() == 1);
|
||||||
|
|
||||||
|
std::thread([&]() // join below makes this ok
|
||||||
|
{
|
||||||
|
SetThreadDescription(GetCurrentThread(), L"STA");
|
||||||
|
auto coUninit = platform::CoInitializeEx(COINIT_APARTMENTTHREADED);
|
||||||
|
std::ignore = av1.get_or_create(fn);
|
||||||
|
REQUIRE(av1.storage().size() == 2);
|
||||||
|
REQUIRE(av1.current_apartment_variable_count() == 1);
|
||||||
|
}).join();
|
||||||
|
REQUIRE(av1.storage().size() == 1);
|
||||||
|
|
||||||
|
av1.get_or_create(fn)++;
|
||||||
|
v = av1.get_existing();
|
||||||
|
REQUIRE(v == 43);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED);
|
||||||
|
|
||||||
|
std::ignore = av1.get_or_create(fn);
|
||||||
|
REQUIRE(av1.current_apartment_variable_count() == 1);
|
||||||
|
int i = 1;
|
||||||
|
av1.set(i);
|
||||||
|
av1.clear();
|
||||||
|
REQUIRE(av1.current_apartment_variable_count() == 0);
|
||||||
|
|
||||||
|
// will fail fast since clear() was called.
|
||||||
|
// av1.set(1);
|
||||||
|
av1.clear_all_apartments_async().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE(av1.storage().size() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename platform = wil::apartment_variable_platform>
|
||||||
|
void TestMultipleApartments()
|
||||||
|
{
|
||||||
|
wil::apartment_variable<int, wil::apartment_variable_leak_action::fail_fast, platform> av1, av2;
|
||||||
|
|
||||||
|
wil::unique_event t1Created{ wil::EventOptions::None }, t2Created{ wil::EventOptions::None };
|
||||||
|
wil::unique_event t1Shutdown{ wil::EventOptions::None }, t2Shutdown{ wil::EventOptions::None };
|
||||||
|
|
||||||
|
auto apt1_thread = std::thread([&]() // join below makes this ok
|
||||||
|
{
|
||||||
|
SetThreadDescription(GetCurrentThread(), L"STA 1");
|
||||||
|
auto coUninit = platform::CoInitializeEx(COINIT_APARTMENTTHREADED);
|
||||||
|
std::ignore = av1.get_or_create(fn);
|
||||||
|
std::ignore = av2.get_or_create(fn);
|
||||||
|
t1Created.SetEvent();
|
||||||
|
co_wait(t1Shutdown);
|
||||||
|
});
|
||||||
|
|
||||||
|
auto apt2_thread = std::thread([&]() // join below makes this ok
|
||||||
|
{
|
||||||
|
SetThreadDescription(GetCurrentThread(), L"STA 2");
|
||||||
|
auto coUninit = platform::CoInitializeEx(COINIT_APARTMENTTHREADED);
|
||||||
|
std::ignore = av1.get_or_create(fn);
|
||||||
|
std::ignore = av2.get_or_create(fn);
|
||||||
|
t2Created.SetEvent();
|
||||||
|
co_wait(t2Shutdown);
|
||||||
|
});
|
||||||
|
|
||||||
|
t1Created.wait();
|
||||||
|
t2Created.wait();
|
||||||
|
av1.clear_all_apartments_async().get();
|
||||||
|
av2.clear_all_apartments_async().get();
|
||||||
|
|
||||||
|
t1Shutdown.SetEvent();
|
||||||
|
t2Shutdown.SetEvent();
|
||||||
|
|
||||||
|
apt1_thread.join();
|
||||||
|
apt2_thread.join();
|
||||||
|
|
||||||
|
REQUIRE((wil::apartment_variable<int, wil::apartment_variable_leak_action::fail_fast, platform>::storage().size() == 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename platform = wil::apartment_variable_platform>
|
||||||
|
void TestWinningApartmentAlreadyRundownRace()
|
||||||
|
{
|
||||||
|
auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED);
|
||||||
|
|
||||||
|
wil::apartment_variable<int, wil::apartment_variable_leak_action::fail_fast, platform> av;
|
||||||
|
|
||||||
|
std::ignore = av.get_or_create(fn);
|
||||||
|
const auto& storage = av.storage(); // for viewing the storage in the debugger
|
||||||
|
|
||||||
|
wil::unique_event otherAptVarCreated{ wil::EventOptions::None };
|
||||||
|
wil::unique_event startApartmentRundown{ wil::EventOptions::None };
|
||||||
|
wil::unique_event comRundownComplete{ wil::EventOptions::None };
|
||||||
|
|
||||||
|
auto apt_thread = std::thread([&]() // join below makes this ok
|
||||||
|
{
|
||||||
|
SetThreadDescription(GetCurrentThread(), L"STA");
|
||||||
|
auto coUninit = platform::CoInitializeEx(COINIT_APARTMENTTHREADED);
|
||||||
|
std::ignore = av.get_or_create(fn);
|
||||||
|
otherAptVarCreated.SetEvent();
|
||||||
|
co_wait(startApartmentRundown);
|
||||||
|
});
|
||||||
|
|
||||||
|
otherAptVarCreated.wait();
|
||||||
|
// we now have av in this apartment and in the STA
|
||||||
|
REQUIRE(storage.size() == 2);
|
||||||
|
// wait for async clean to complete
|
||||||
|
av.clear_all_apartments_async().get();
|
||||||
|
startApartmentRundown.SetEvent();
|
||||||
|
|
||||||
|
REQUIRE(av.storage().size() == 0);
|
||||||
|
apt_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename platform = wil::apartment_variable_platform>
|
||||||
|
void TestLosingApartmentAlreadyRundownRace()
|
||||||
|
{
|
||||||
|
auto coUninit = platform::CoInitializeEx(COINIT_MULTITHREADED);
|
||||||
|
|
||||||
|
wil::apartment_variable<int, wil::apartment_variable_leak_action::fail_fast, platform> av;
|
||||||
|
|
||||||
|
std::ignore = av.get_or_create(fn);
|
||||||
|
const auto& storage = av.storage(); // for viewing the storage in the debugger
|
||||||
|
|
||||||
|
wil::unique_event otherAptVarCreated{ wil::EventOptions::None };
|
||||||
|
wil::unique_event startApartmentRundown{ wil::EventOptions::None };
|
||||||
|
wil::unique_event comRundownComplete{ wil::EventOptions::None };
|
||||||
|
|
||||||
|
auto apt_thread = std::thread([&]() // join below makes this ok
|
||||||
|
{
|
||||||
|
SetThreadDescription(GetCurrentThread(), L"STA");
|
||||||
|
auto coUninit = platform::CoInitializeEx(COINIT_APARTMENTTHREADED);
|
||||||
|
std::ignore = av.get_or_create(fn);
|
||||||
|
otherAptVarCreated.SetEvent();
|
||||||
|
co_wait(startApartmentRundown);
|
||||||
|
coUninit.reset();
|
||||||
|
comRundownComplete.SetEvent();
|
||||||
|
});
|
||||||
|
|
||||||
|
otherAptVarCreated.wait();
|
||||||
|
// we now have av in this apartment and in the STA
|
||||||
|
REQUIRE(storage.size() == 2);
|
||||||
|
auto clearAllOperation = av.clear_all_apartments_async();
|
||||||
|
startApartmentRundown.SetEvent();
|
||||||
|
comRundownComplete.wait();
|
||||||
|
clearAllOperation.get(); // wait for the async rundowns to complete
|
||||||
|
|
||||||
|
REQUIRE(av.storage().size() == 0);
|
||||||
|
apt_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ComApartmentVariable::ShutdownRegistration", "[LocalOnly][com][unique_apartment_shutdown_registration]")
|
||||||
|
{
|
||||||
|
{
|
||||||
|
wil::unique_apartment_shutdown_registration r;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto coUninit = wil::CoInitializeEx(COINIT_MULTITHREADED);
|
||||||
|
|
||||||
|
struct ApartmentObserver : public winrt::implements<ApartmentObserver, IApartmentShutdown>
|
||||||
|
{
|
||||||
|
void STDMETHODCALLTYPE OnUninitialize(unsigned long long apartmentId) noexcept override
|
||||||
|
{
|
||||||
|
LogOutput(L"OnUninitialize %ull\n", apartmentId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
wil::unique_apartment_shutdown_registration apt_shutdown_registration;
|
||||||
|
unsigned long long id{};
|
||||||
|
REQUIRE_SUCCEEDED(::RoRegisterForApartmentShutdown(winrt::make<ApartmentObserver>().get(), &id, apt_shutdown_registration.put()));
|
||||||
|
LogOutput(L"RoRegisterForApartmentShutdown %p\r\n", apt_shutdown_registration.get());
|
||||||
|
// don't unregister and let the pending COM apartment rundown invoke the callback.
|
||||||
|
apt_shutdown_registration.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ComApartmentVariable::CallAllMethods", "[com][apartment_variable]")
|
||||||
|
{
|
||||||
|
RunApartmentVariableTest(TestApartmentVariableAllMethods<mock_platform>);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ComApartmentVariable::GetOrCreateForms", "[com][apartment_variable]")
|
||||||
|
{
|
||||||
|
RunApartmentVariableTest(TestApartmentVariableGetOrCreateForms<mock_platform>);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ComApartmentVariable::VariableLifetimes", "[com][apartment_variable]")
|
||||||
|
{
|
||||||
|
RunApartmentVariableTest(TestApartmentVariableLifetimes<mock_platform>);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ComApartmentVariable::WinningApartmentAlreadyRundownRace", "[com][apartment_variable]")
|
||||||
|
{
|
||||||
|
RunApartmentVariableTest(TestWinningApartmentAlreadyRundownRace<mock_platform>);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ComApartmentVariable::LosingApartmentAlreadyRundownRace", "[com][apartment_variable]")
|
||||||
|
{
|
||||||
|
RunApartmentVariableTest(TestLosingApartmentAlreadyRundownRace<mock_platform>);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ComApartmentVariable::MultipleApartments", "[com][apartment_variable]")
|
||||||
|
{
|
||||||
|
RunApartmentVariableTest(TestMultipleApartments<mock_platform>);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ComApartmentVariable::UseRealPlatformRunAllTests", "[com][apartment_variable]")
|
||||||
|
{
|
||||||
|
if (!wil::are_apartment_variables_supported())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RunApartmentVariableTest(TestApartmentVariableAllMethods);
|
||||||
|
RunApartmentVariableTest(TestApartmentVariableGetOrCreateForms);
|
||||||
|
RunApartmentVariableTest(TestApartmentVariableLifetimes);
|
||||||
|
RunApartmentVariableTest(TestWinningApartmentAlreadyRundownRace);
|
||||||
|
RunApartmentVariableTest(TestLosingApartmentAlreadyRundownRace);
|
||||||
|
RunApartmentVariableTest(TestMultipleApartments);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -6,6 +6,8 @@
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
|
#include <Bits.h>
|
||||||
|
|
||||||
using namespace Microsoft::WRL;
|
using namespace Microsoft::WRL;
|
||||||
|
|
||||||
// avoid including #include <shobjidl.h>, it fails to compile in noprivateapis
|
// avoid including #include <shobjidl.h>, it fails to compile in noprivateapis
|
||||||
|
@ -130,6 +132,25 @@ TEST_CASE("ComTests::Test_Constructors", "[com][com_ptr]")
|
||||||
REQUIRE(ptrMove2.get() == &helper4);
|
REQUIRE(ptrMove2.get() == &helper4);
|
||||||
REQUIRE(ptr2.get() == nullptr);
|
REQUIRE(ptr2.get() == nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__cpp_deduction_guides) && (__cpp_deduction_guides >= 201907L)
|
||||||
|
SECTION("CTAD pointer construction")
|
||||||
|
{
|
||||||
|
wil::com_ptr_nothrow ptr(&helper); // explicit
|
||||||
|
REQUIRE(IUnknownFake::GetAddRef() == 1);
|
||||||
|
REQUIRE(ptr.get() == &helper);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ComTests::Test_Make", "[com][com_ptr]")
|
||||||
|
{
|
||||||
|
IUnknownFake::Clear();
|
||||||
|
IUnknownFake helper;
|
||||||
|
|
||||||
|
auto ptr = wil::make_com_ptr_nothrow(&helper); // CTAD workaround for pre-C++20
|
||||||
|
REQUIRE(IUnknownFake::GetAddRef() == 1);
|
||||||
|
REQUIRE(ptr.get() == &helper);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("ComTests::Test_Assign", "[com][com_ptr]")
|
TEST_CASE("ComTests::Test_Assign", "[com][com_ptr]")
|
||||||
|
@ -2231,6 +2252,90 @@ TEST_CASE("ComTests::VerifyCoGetClassObject", "[com][CoGetClassObject]")
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__IBackgroundCopyManager_INTERFACE_DEFINED__) && (__WI_LIBCPP_STD_VER >= 17)
|
||||||
|
TEST_CASE("ComTests::VerifyCoCreateEx", "[com][CoCreateInstance]")
|
||||||
|
{
|
||||||
|
auto init = wil::CoInitializeEx_failfast();
|
||||||
|
|
||||||
|
{
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
auto [sp1, ps1] = wil::CoCreateInstanceEx<IBackgroundCopyManager, IUnknown>(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER);
|
||||||
|
REQUIRE((sp1 && ps1));
|
||||||
|
#endif
|
||||||
|
auto [hr, unk] = wil::CoCreateInstanceExNoThrow<IBackgroundCopyManager, IUnknown>(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER);
|
||||||
|
REQUIRE_SUCCEEDED(hr);
|
||||||
|
auto sp = std::get<0>(unk);
|
||||||
|
auto ps = std::get<1>(unk);
|
||||||
|
REQUIRE((sp && ps));
|
||||||
|
auto [sp3, ps3] = wil::CoCreateInstanceExFailFast<IBackgroundCopyManager, IUnknown>(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER);
|
||||||
|
REQUIRE((sp3 && ps3));
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
{
|
||||||
|
auto [ps, pf] = wil::CoCreateInstanceEx<IPersistStream, IPersistFile>(__uuidof(ShellLink), CLSCTX_INPROC_SERVER);
|
||||||
|
std::ignore = ps->IsDirty();
|
||||||
|
std::ignore = pf->IsDirty();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ComTests::VerifyCoCreateInstanceExNoThrowMissingInterface", "[com][CoCreateInstance]")
|
||||||
|
{
|
||||||
|
auto init = wil::CoInitializeEx_failfast();
|
||||||
|
|
||||||
|
{
|
||||||
|
// IPropertyBag is not implemented
|
||||||
|
auto [error, result] = wil::CoCreateInstanceExNoThrow<IBackgroundCopyManager, IUnknown, IPropertyBag>
|
||||||
|
(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER);
|
||||||
|
REQUIRE(error == E_NOINTERFACE);
|
||||||
|
REQUIRE(std::get<0>(result).get() == nullptr);
|
||||||
|
REQUIRE(std::get<1>(result).get() == nullptr);
|
||||||
|
REQUIRE(std::get<2>(result).get() == nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ComTests::VerifyTryCoCreateInstanceMissingInterface", "[com][CoCreateInstance]")
|
||||||
|
{
|
||||||
|
auto init = wil::CoInitializeEx_failfast();
|
||||||
|
|
||||||
|
// request some implemented, one not (IPropertyBag), partial results enabled
|
||||||
|
{
|
||||||
|
auto [sp, pb] = wil::TryCoCreateInstanceEx<IBackgroundCopyManager, IPropertyBag>
|
||||||
|
(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER);
|
||||||
|
REQUIRE(sp != nullptr);
|
||||||
|
REQUIRE(pb == nullptr);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto [sp, pb] = wil::TryCoCreateInstanceExNoThrow<IBackgroundCopyManager, IPropertyBag>
|
||||||
|
(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER);
|
||||||
|
REQUIRE(sp != nullptr);
|
||||||
|
REQUIRE(pb == nullptr);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto [sp, pb] = wil::TryCoCreateInstanceExFailFast<IBackgroundCopyManager, IPropertyBag>
|
||||||
|
(__uuidof(BackgroundCopyManager), CLSCTX_LOCAL_SERVER);
|
||||||
|
REQUIRE(sp != nullptr);
|
||||||
|
REQUIRE(pb == nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
TEST_CASE("ComTests::VerifyQueryMultipleInterfaces", "[com][com_multi_query]")
|
||||||
|
{
|
||||||
|
auto init = wil::CoInitializeEx_failfast();
|
||||||
|
|
||||||
|
auto mgr = wil::CoCreateInstance<BackgroundCopyManager>(CLSCTX_LOCAL_SERVER);
|
||||||
|
auto [sp, ps] = wil::com_multi_query<IBackgroundCopyManager, IUnknown>(mgr.get());
|
||||||
|
REQUIRE(sp);
|
||||||
|
REQUIRE(ps);
|
||||||
|
auto [sp1, pb] = wil::try_com_multi_query<IBackgroundCopyManager, IPropertyBag>(mgr.get());
|
||||||
|
REQUIRE(sp1);
|
||||||
|
REQUIRE(!pb);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif // __IBackgroundCopyManager_INTERFACE_DEFINED__
|
||||||
|
|
||||||
#ifdef __IObjectWithSite_INTERFACE_DEFINED__
|
#ifdef __IObjectWithSite_INTERFACE_DEFINED__
|
||||||
TEST_CASE("ComTests::VerifyComSetSiteNullIsMoveOnly", "[com][com_set_site]")
|
TEST_CASE("ComTests::VerifyComSetSiteNullIsMoveOnly", "[com][com_set_site]")
|
||||||
{
|
{
|
||||||
|
|
|
@ -159,7 +159,7 @@ enum class EClassTest
|
||||||
};
|
};
|
||||||
DEFINE_ENUM_FLAG_OPERATORS(EClassTest);
|
DEFINE_ENUM_FLAG_OPERATORS(EClassTest);
|
||||||
|
|
||||||
enum ERawTest
|
enum ERawTest : unsigned int
|
||||||
{
|
{
|
||||||
ER_None = 0x0,
|
ER_None = 0x0,
|
||||||
ER_One = 0x1,
|
ER_One = 0x1,
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
// However, since we're going to link into the same executable as 'CppWinRTTests.cpp', the 'winrt_to_hresult_handler'
|
// However, since we're going to link into the same executable as 'CppWinRTTests.cpp', the 'winrt_to_hresult_handler'
|
||||||
// global function pointer should be set, so these should all run successfully
|
// global function pointer should be set, so these should all run successfully
|
||||||
|
|
||||||
|
#include <inspectable.h> // Must be included before base.h
|
||||||
|
|
||||||
#include <winrt/base.h>
|
#include <winrt/base.h>
|
||||||
#include <wil/result.h>
|
#include <wil/result.h>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
|
|
||||||
#include <wil/cppwinrt.h>
|
#include <wil/cppwinrt.h>
|
||||||
|
#include <winrt/Windows.Foundation.h>
|
||||||
|
#include <winrt/Windows.Foundation.Collections.h>
|
||||||
|
#include <wil/cppwinrt_helpers.h>
|
||||||
|
#include <winrt/Windows.System.h>
|
||||||
|
#include <wil/cppwinrt_helpers.h> // Verify can include a second time to unlock more features
|
||||||
|
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
|
||||||
|
@ -22,6 +27,145 @@ static const HRESULT cppwinrt_mapped_hresults[] =
|
||||||
E_OUTOFMEMORY,
|
E_OUTOFMEMORY,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T> auto copy_thing(T const& src)
|
||||||
|
{
|
||||||
|
return std::decay_t<T>(src);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename K>
|
||||||
|
void CheckMapVector(std::vector<winrt::Windows::Foundation::Collections::IKeyValuePair<T, K>> const& test, std::map<T, K> const& src)
|
||||||
|
{
|
||||||
|
REQUIRE(test.size() == src.size());
|
||||||
|
for (auto&& i : test)
|
||||||
|
{
|
||||||
|
REQUIRE(i.Value() == src.at(i.Key()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct vector_like
|
||||||
|
{
|
||||||
|
uint32_t Size() const { return 100; }
|
||||||
|
int GetAt(uint32_t) const { return 15; }
|
||||||
|
|
||||||
|
uint32_t GetMany(uint32_t start, winrt::array_view<int> items) const
|
||||||
|
{
|
||||||
|
if (start > 0)
|
||||||
|
{
|
||||||
|
throw winrt::hresult_out_of_bounds();
|
||||||
|
}
|
||||||
|
uint32_t const to_fill = (std::min)(items.size(), Size());
|
||||||
|
std::fill_n(items.begin(), to_fill, GetAt(0));
|
||||||
|
return to_fill;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct iterator_like
|
||||||
|
{
|
||||||
|
static const uint32_t total = 20;
|
||||||
|
mutable uint32_t remaining = total;
|
||||||
|
int Current() const { return 3; }
|
||||||
|
|
||||||
|
uint32_t GetMany(winrt::array_view<int> items) const
|
||||||
|
{
|
||||||
|
auto to_copy = (std::min)(items.size(), remaining);
|
||||||
|
std::fill_n(items.begin(), to_copy, Current());
|
||||||
|
remaining -= to_copy;
|
||||||
|
return to_copy;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct iterable_like
|
||||||
|
{
|
||||||
|
auto First() const { return iterator_like{}; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct unstable_vector : winrt::implements<unstable_vector, winrt::Windows::Foundation::Collections::IVectorView<int>>
|
||||||
|
{
|
||||||
|
auto Size() { return 4; }
|
||||||
|
int GetAt(uint32_t) { return 7; }
|
||||||
|
|
||||||
|
uint32_t GetMany(uint32_t, winrt::array_view<int> items)
|
||||||
|
{
|
||||||
|
std::fill(items.begin(), items.end(), GetAt(0));
|
||||||
|
return items.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IndexOf(int, uint32_t) { throw winrt::hresult_not_implemented(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE("CppWinRTTests::VectorToVector", "[cppwinrt]")
|
||||||
|
{
|
||||||
|
winrt::init_apartment();
|
||||||
|
{
|
||||||
|
std::vector<winrt::hstring> src_vector = { L"foo", L"bar", L"bas" };
|
||||||
|
auto sv = winrt::single_threaded_vector(copy_thing(src_vector));
|
||||||
|
REQUIRE(wil::to_vector(sv) == src_vector);
|
||||||
|
REQUIRE(wil::to_vector(sv.GetView()) == src_vector);
|
||||||
|
REQUIRE(wil::to_vector(sv.First()) == src_vector);
|
||||||
|
REQUIRE(wil::to_vector(sv.First()) == src_vector);
|
||||||
|
REQUIRE(wil::to_vector(sv.as<winrt::Windows::Foundation::Collections::IIterable<winrt::hstring>>()) == src_vector);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::vector<uint32_t> src_vector = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 };
|
||||||
|
auto sv = winrt::single_threaded_vector(copy_thing(src_vector));
|
||||||
|
REQUIRE(wil::to_vector(sv) == src_vector);
|
||||||
|
REQUIRE(wil::to_vector(sv.GetView()) == src_vector);
|
||||||
|
REQUIRE(wil::to_vector(sv.First()) == src_vector);
|
||||||
|
REQUIRE(wil::to_vector(sv.as<winrt::Windows::Foundation::Collections::IIterable<uint32_t>>()) == src_vector);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::vector<float> src_vector;
|
||||||
|
auto sv = winrt::single_threaded_vector(copy_thing(src_vector));
|
||||||
|
REQUIRE(wil::to_vector(sv) == src_vector);
|
||||||
|
REQUIRE(wil::to_vector(sv.GetView()) == src_vector);
|
||||||
|
REQUIRE(wil::to_vector(sv.First()) == src_vector);
|
||||||
|
REQUIRE(wil::to_vector(sv.as<winrt::Windows::Foundation::Collections::IIterable<float>>()) == src_vector);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::map<winrt::hstring, winrt::hstring> src_map{{L"kittens", L"fluffy"}, {L"puppies", L"cute"}};
|
||||||
|
auto sm = winrt::single_threaded_map(copy_thing(src_map));
|
||||||
|
CheckMapVector(wil::to_vector(sm), src_map);
|
||||||
|
CheckMapVector(wil::to_vector(sm.GetView()), src_map);
|
||||||
|
CheckMapVector(wil::to_vector(sm.First()), src_map);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
winrt::Windows::Foundation::Collections::PropertySet props;
|
||||||
|
props.Insert(L"kitten", winrt::box_value(L"fluffy"));
|
||||||
|
props.Insert(L"puppy", winrt::box_value<uint32_t>(25));
|
||||||
|
auto converted = wil::to_vector(props);
|
||||||
|
REQUIRE(converted.size() == props.Size());
|
||||||
|
for (auto&& kv : converted)
|
||||||
|
{
|
||||||
|
if (kv.Key() == L"kitten")
|
||||||
|
{
|
||||||
|
REQUIRE(kv.Value().as<winrt::hstring>() == L"fluffy");
|
||||||
|
}
|
||||||
|
else if (kv.Key() == L"puppy")
|
||||||
|
{
|
||||||
|
REQUIRE(kv.Value().as<uint32_t>() == 25);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
REQUIRE(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
REQUIRE_THROWS(wil::to_vector(winrt::make<unstable_vector>()));
|
||||||
|
|
||||||
|
auto ilike = wil::to_vector(iterable_like{});
|
||||||
|
REQUIRE(ilike.size() == iterator_like::total);
|
||||||
|
for (auto&& i : ilike) REQUIRE(i == iterator_like{}.Current());
|
||||||
|
|
||||||
|
auto vlike = wil::to_vector(vector_like{});
|
||||||
|
REQUIRE(vlike.size() == vector_like{}.Size());
|
||||||
|
for (auto&& i : vlike) REQUIRE(i == vector_like{}.GetAt(0));
|
||||||
|
|
||||||
|
winrt::clear_factory_cache();
|
||||||
|
winrt::uninit_apartment();
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("CppWinRTTests::WilToCppWinRTExceptionTranslationTest", "[cppwinrt]")
|
TEST_CASE("CppWinRTTests::WilToCppWinRTExceptionTranslationTest", "[cppwinrt]")
|
||||||
{
|
{
|
||||||
auto test = [](HRESULT hr)
|
auto test = [](HRESULT hr)
|
||||||
|
@ -142,3 +286,159 @@ TEST_CASE("CppWinRTTests::CppWinRTConsistencyTest", "[cppwinrt]")
|
||||||
// NOTE: C++/WinRT maps other 'std::exception' derived exceptions to E_FAIL, however we preserve the WIL behavior
|
// NOTE: C++/WinRT maps other 'std::exception' derived exceptions to E_FAIL, however we preserve the WIL behavior
|
||||||
// that such exceptions become HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)
|
// that such exceptions become HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CppWinRTTests::ModuleReference", "[cppwinrt]")
|
||||||
|
{
|
||||||
|
auto peek_module_ref_count = []()
|
||||||
|
{
|
||||||
|
++winrt::get_module_lock();
|
||||||
|
return --winrt::get_module_lock();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto initial = peek_module_ref_count();
|
||||||
|
|
||||||
|
// Basic test: Construct and destruct.
|
||||||
|
{
|
||||||
|
auto module_ref = wil::winrt_module_reference();
|
||||||
|
REQUIRE(peek_module_ref_count() == initial + 1);
|
||||||
|
}
|
||||||
|
REQUIRE(peek_module_ref_count() == initial);
|
||||||
|
|
||||||
|
// Fancy test: Copy object with embedded reference.
|
||||||
|
{
|
||||||
|
struct object_with_ref
|
||||||
|
{
|
||||||
|
wil::winrt_module_reference ref;
|
||||||
|
};
|
||||||
|
object_with_ref o1;
|
||||||
|
REQUIRE(peek_module_ref_count() == initial + 1);
|
||||||
|
auto o2 = o1;
|
||||||
|
REQUIRE(peek_module_ref_count() == initial + 2);
|
||||||
|
o1 = o2;
|
||||||
|
REQUIRE(peek_module_ref_count() == initial + 2);
|
||||||
|
o2 = std::move(o1);
|
||||||
|
REQUIRE(peek_module_ref_count() == initial + 2);
|
||||||
|
}
|
||||||
|
REQUIRE(peek_module_ref_count() == initial);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (!defined(__clang__) && defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L)) || defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
|
||||||
|
|
||||||
|
// Define our own custom dispatcher that we can force it to behave in certain ways.
|
||||||
|
// wil::resume_foreground supports any dispatcher that has a dispatcher_traits.
|
||||||
|
|
||||||
|
namespace test
|
||||||
|
{
|
||||||
|
enum class TestDispatcherPriority
|
||||||
|
{
|
||||||
|
Normal = 0,
|
||||||
|
Weird = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
using TestDispatcherHandler = winrt::delegate<>;
|
||||||
|
|
||||||
|
enum class TestDispatcherMode
|
||||||
|
{
|
||||||
|
Dispatch,
|
||||||
|
RaceDispatch,
|
||||||
|
Orphan,
|
||||||
|
Fail,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestDispatcher
|
||||||
|
{
|
||||||
|
TestDispatcher() = default;
|
||||||
|
TestDispatcher(TestDispatcher const&) = delete;
|
||||||
|
|
||||||
|
TestDispatcherMode mode = TestDispatcherMode::Dispatch;
|
||||||
|
TestDispatcherPriority expected_priority = TestDispatcherPriority::Normal;
|
||||||
|
|
||||||
|
void TryEnqueue(TestDispatcherPriority priority, TestDispatcherHandler const& handler) const
|
||||||
|
{
|
||||||
|
REQUIRE(priority == expected_priority);
|
||||||
|
|
||||||
|
if (mode == TestDispatcherMode::Fail)
|
||||||
|
{
|
||||||
|
throw winrt::hresult_not_implemented();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode == TestDispatcherMode::RaceDispatch)
|
||||||
|
{
|
||||||
|
handler();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ignore = [](auto mode, auto handler) ->winrt::fire_and_forget
|
||||||
|
{
|
||||||
|
co_await winrt::resume_background();
|
||||||
|
if (mode == TestDispatcherMode::Dispatch)
|
||||||
|
{
|
||||||
|
handler();
|
||||||
|
}
|
||||||
|
}(mode, handler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace wil::details
|
||||||
|
{
|
||||||
|
template<>
|
||||||
|
struct dispatcher_traits<test::TestDispatcher>
|
||||||
|
{
|
||||||
|
using Priority = test::TestDispatcherPriority;
|
||||||
|
using Handler = test::TestDispatcherHandler;
|
||||||
|
using Scheduler = dispatcher_TryEnqueue;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("CppWinRTTests::ResumeForegroundTests", "[cppwinrt]")
|
||||||
|
{
|
||||||
|
// Verify that the DispatcherQueue version has been unlocked.
|
||||||
|
using Verify = decltype(wil::resume_foreground(winrt::Windows::System::DispatcherQueue{ nullptr }));
|
||||||
|
static_assert(wistd::is_trivial_v<Verify> || !wistd::is_trivial_v<Verify>);
|
||||||
|
|
||||||
|
[]() -> winrt::Windows::Foundation::IAsyncAction
|
||||||
|
{
|
||||||
|
test::TestDispatcher dispatcher;
|
||||||
|
|
||||||
|
// Normal case: Resumes on new thread.
|
||||||
|
dispatcher.mode = test::TestDispatcherMode::Dispatch;
|
||||||
|
co_await wil::resume_foreground(dispatcher);
|
||||||
|
|
||||||
|
// Race case: Resumes before TryEnqueue returns.
|
||||||
|
dispatcher.mode = test::TestDispatcherMode::RaceDispatch;
|
||||||
|
co_await wil::resume_foreground(dispatcher);
|
||||||
|
|
||||||
|
// Orphan case: Never resumes, detected when handler is destructed without ever being invoked.
|
||||||
|
dispatcher.mode = test::TestDispatcherMode::Orphan;
|
||||||
|
bool seen = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
co_await wil::resume_foreground(dispatcher);
|
||||||
|
}
|
||||||
|
catch (winrt::hresult_error const& e)
|
||||||
|
{
|
||||||
|
seen = e.code() == HRESULT_FROM_WIN32(HRESULT_FROM_WIN32(ERROR_NO_TASK_QUEUE));
|
||||||
|
}
|
||||||
|
REQUIRE(seen);
|
||||||
|
|
||||||
|
// Fail case: Can't even schedule the resumption.
|
||||||
|
dispatcher.mode = test::TestDispatcherMode::Fail;
|
||||||
|
seen = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
co_await wil::resume_foreground(dispatcher);
|
||||||
|
}
|
||||||
|
catch (winrt::hresult_not_implemented const&)
|
||||||
|
{
|
||||||
|
seen = true;
|
||||||
|
}
|
||||||
|
REQUIRE(seen);
|
||||||
|
|
||||||
|
// Custom priority.
|
||||||
|
dispatcher.mode = test::TestDispatcherMode::Dispatch;
|
||||||
|
dispatcher.expected_priority = test::TestDispatcherPriority::Weird;
|
||||||
|
co_await wil::resume_foreground(dispatcher, test::TestDispatcherPriority::Weird);
|
||||||
|
}().get();
|
||||||
|
}
|
||||||
|
#endif // coroutines
|
||||||
|
|
|
@ -42,7 +42,7 @@ struct WinRTStorage<HSTRING>
|
||||||
{
|
{
|
||||||
Microsoft::WRL::Wrappers::HString value;
|
Microsoft::WRL::Wrappers::HString value;
|
||||||
|
|
||||||
HRESULT CopyTo(HSTRING* result)
|
HRESULT CopyTo(HSTRING* result) const
|
||||||
{
|
{
|
||||||
return value.CopyTo(result);
|
return value.CopyTo(result);
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ struct WinRTStorage<HSTRING>
|
||||||
value = {};
|
value = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Equals(HSTRING val)
|
bool Equals(HSTRING val) const
|
||||||
{
|
{
|
||||||
return value == val;
|
return value == val;
|
||||||
}
|
}
|
||||||
|
@ -207,7 +207,7 @@ private:
|
||||||
WinRTStorage<Abi> m_storage;
|
WinRTStorage<Abi> m_storage;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Logical, typename Abi = Logical, size_t MaxSize = 42>
|
template <typename Logical, typename Abi = Logical, size_t MaxSize = 250>
|
||||||
struct FakeVector : Microsoft::WRL::RuntimeClass<
|
struct FakeVector : Microsoft::WRL::RuntimeClass<
|
||||||
ABI::Windows::Foundation::Collections::IVector<Logical>,
|
ABI::Windows::Foundation::Collections::IVector<Logical>,
|
||||||
ABI::Windows::Foundation::Collections::IVectorView<Logical>>
|
ABI::Windows::Foundation::Collections::IVectorView<Logical>>
|
||||||
|
@ -292,7 +292,7 @@ struct FakeVector : Microsoft::WRL::RuntimeClass<
|
||||||
|
|
||||||
for (size_t i = index + 1; i < m_size; ++i)
|
for (size_t i = index + 1; i < m_size; ++i)
|
||||||
{
|
{
|
||||||
wistd::swap_wil(m_data[i - 1], m_data[i]);
|
wistd::swap_wil(m_data[i], m_data[i - 1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_data[--m_size].Reset();
|
m_data[--m_size].Reset();
|
||||||
|
@ -349,8 +349,8 @@ struct FakeVector : Microsoft::WRL::RuntimeClass<
|
||||||
count = (count > capacity) ? capacity : count;
|
count = (count > capacity) ? capacity : count;
|
||||||
|
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
unsigned i = 0;
|
unsigned i;
|
||||||
for (; (i < count) && SUCCEEDED(hr); ++i)
|
for (i = 0; (i < count) && SUCCEEDED(hr); ++i)
|
||||||
{
|
{
|
||||||
hr = m_data[startIndex + i].CopyTo(value + i);
|
hr = m_data[startIndex + i].CopyTo(value + i);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,13 @@ bool DirectoryExists(_In_ PCWSTR path)
|
||||||
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
|
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FileExists(_In_ PCWSTR path)
|
||||||
|
{
|
||||||
|
DWORD dwAttrib = GetFileAttributesW(path);
|
||||||
|
|
||||||
|
return (dwAttrib != INVALID_FILE_ATTRIBUTES);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("FileSystemTests::CreateDirectory", "[filesystem]")
|
TEST_CASE("FileSystemTests::CreateDirectory", "[filesystem]")
|
||||||
{
|
{
|
||||||
wchar_t basePath[MAX_PATH];
|
wchar_t basePath[MAX_PATH];
|
||||||
|
@ -80,6 +87,99 @@ TEST_CASE("FileSystemTests::CreateDirectory", "[filesystem]")
|
||||||
REQUIRE_FALSE(DirectoryExists(absoluteTestPath4));
|
REQUIRE_FALSE(DirectoryExists(absoluteTestPath4));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("FileSystemTests::VerifyRemoveDirectoryRecursiveDoesNotTraverseWithoutAHandle", "[filesystem]")
|
||||||
|
{
|
||||||
|
auto CreateRelativePath = [](PCWSTR root, PCWSTR name)
|
||||||
|
{
|
||||||
|
wil::unique_hlocal_string path;
|
||||||
|
REQUIRE_SUCCEEDED(PathAllocCombine(root, name, PATHCCH_ALLOW_LONG_PATHS, &path));
|
||||||
|
return path;
|
||||||
|
};
|
||||||
|
|
||||||
|
wil::unique_cotaskmem_string tempPath;
|
||||||
|
REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(LR"(%TEMP%)", tempPath));
|
||||||
|
const auto basePath = CreateRelativePath(tempPath.get(), L"FileSystemTests");
|
||||||
|
REQUIRE_SUCCEEDED(wil::CreateDirectoryDeepNoThrow(basePath.get()));
|
||||||
|
|
||||||
|
auto scopeGuard = wil::scope_exit([&]
|
||||||
|
{
|
||||||
|
wil::RemoveDirectoryRecursiveNoThrow(basePath.get());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Try to delete a directory whose handle is already taken.
|
||||||
|
const auto folderToRecurse = CreateRelativePath(basePath.get(), L"folderToRecurse");
|
||||||
|
REQUIRE(::CreateDirectoryW(folderToRecurse.get(), nullptr));
|
||||||
|
|
||||||
|
const auto subfolderWithHandle = CreateRelativePath(folderToRecurse.get(), L"subfolderWithHandle");
|
||||||
|
REQUIRE(::CreateDirectoryW(subfolderWithHandle.get(), nullptr));
|
||||||
|
|
||||||
|
const auto childOfSubfolder = CreateRelativePath(subfolderWithHandle.get(), L"childOfSubfolder");
|
||||||
|
REQUIRE(::CreateDirectoryW(childOfSubfolder.get(), nullptr));
|
||||||
|
|
||||||
|
// Passing a 0 in share flags only allows metadata query on this file by other processes.
|
||||||
|
// This should fail with a sharing violation error when any other action is taken.
|
||||||
|
wil::unique_hfile subFolderHandle(::CreateFileW(subfolderWithHandle.get(), GENERIC_ALL,
|
||||||
|
0, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr));
|
||||||
|
REQUIRE(subFolderHandle);
|
||||||
|
|
||||||
|
REQUIRE(wil::RemoveDirectoryRecursiveNoThrow(folderToRecurse.get()) == HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION));
|
||||||
|
|
||||||
|
// Release the handle to allow cleanup.
|
||||||
|
subFolderHandle.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("FileSystemTests::VerifyRemoveDirectoryRecursiveCanDeleteReadOnlyFiles", "[filesystem]")
|
||||||
|
{
|
||||||
|
auto CreateRelativePath = [](PCWSTR root, PCWSTR name)
|
||||||
|
{
|
||||||
|
wil::unique_hlocal_string path;
|
||||||
|
REQUIRE_SUCCEEDED(PathAllocCombine(root, name, PATHCCH_ALLOW_LONG_PATHS, &path));
|
||||||
|
return path;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto CreateReadOnlyFile = [](PCWSTR path)
|
||||||
|
{
|
||||||
|
wil::unique_hfile fileHandle(CreateFileW(path, 0,
|
||||||
|
0, nullptr, CREATE_ALWAYS,
|
||||||
|
FILE_ATTRIBUTE_READONLY, nullptr));
|
||||||
|
REQUIRE(fileHandle);
|
||||||
|
};
|
||||||
|
|
||||||
|
wil::unique_cotaskmem_string tempPath;
|
||||||
|
REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(LR"(%TEMP%)", tempPath));
|
||||||
|
const auto basePath = CreateRelativePath(tempPath.get(), L"FileSystemTests");
|
||||||
|
REQUIRE_SUCCEEDED(wil::CreateDirectoryDeepNoThrow(basePath.get()));
|
||||||
|
|
||||||
|
auto scopeGuard = wil::scope_exit([&]
|
||||||
|
{
|
||||||
|
wil::RemoveDirectoryRecursiveNoThrow(basePath.get(), wil::RemoveDirectoryOptions::RemoveReadOnly);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a reparse point and a target folder that shouldn't get deleted
|
||||||
|
auto folderToDelete = CreateRelativePath(basePath.get(), L"folderToDelete");
|
||||||
|
REQUIRE(::CreateDirectoryW(folderToDelete.get(), nullptr));
|
||||||
|
|
||||||
|
auto topLevelReadOnly = CreateRelativePath(folderToDelete.get(), L"topLevelReadOnly.txt");
|
||||||
|
CreateReadOnlyFile(topLevelReadOnly.get());
|
||||||
|
|
||||||
|
auto subLevel = CreateRelativePath(folderToDelete.get(), L"subLevel");
|
||||||
|
REQUIRE(::CreateDirectoryW(subLevel.get(), nullptr));
|
||||||
|
|
||||||
|
auto subLevelReadOnly = CreateRelativePath(subLevel.get(), L"subLevelReadOnly.txt");
|
||||||
|
CreateReadOnlyFile(subLevelReadOnly.get());
|
||||||
|
|
||||||
|
// Delete will fail without the RemoveReadOnlyFlag
|
||||||
|
REQUIRE_FAILED(wil::RemoveDirectoryRecursiveNoThrow(folderToDelete.get()));
|
||||||
|
REQUIRE_SUCCEEDED(wil::RemoveDirectoryRecursiveNoThrow(folderToDelete.get(), wil::RemoveDirectoryOptions::RemoveReadOnly));
|
||||||
|
|
||||||
|
// Verify all files have been deleted
|
||||||
|
REQUIRE_FALSE(FileExists(subLevelReadOnly.get()));
|
||||||
|
REQUIRE_FALSE(DirectoryExists(subLevel.get()));
|
||||||
|
|
||||||
|
REQUIRE_FALSE(FileExists(topLevelReadOnly.get()));
|
||||||
|
REQUIRE_FALSE(DirectoryExists(folderToDelete.get()));
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
// Learn about the Win32 API normalization here: https://blogs.msdn.microsoft.com/jeremykuhne/2016/04/21/path-normalization/
|
// Learn about the Win32 API normalization here: https://blogs.msdn.microsoft.com/jeremykuhne/2016/04/21/path-normalization/
|
||||||
// This test verifies the ability of RemoveDirectoryRecursive to be able to delete files
|
// This test verifies the ability of RemoveDirectoryRecursive to be able to delete files
|
||||||
|
@ -440,8 +540,28 @@ TEST_CASE("FileSystemTests::VerifyGetModuleFileNameW", "[filesystem]")
|
||||||
REQUIRE(wcscmp(path.get(), path2.get()) == 0);
|
REQUIRE(wcscmp(path.get(), path2.get()) == 0);
|
||||||
|
|
||||||
REQUIRE_FAILED(wil::GetModuleFileNameW((HMODULE)INVALID_HANDLE_VALUE, path));
|
REQUIRE_FAILED(wil::GetModuleFileNameW((HMODULE)INVALID_HANDLE_VALUE, path));
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
auto wstringPath = wil::GetModuleFileNameW<std::wstring, 15>(nullptr);
|
||||||
|
REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
wil::unique_cotaskmem_string NativeGetModuleFileNameWrap(HANDLE processHandle, HMODULE moduleHandle)
|
||||||
|
{
|
||||||
|
DWORD size = MAX_PATH * 4;
|
||||||
|
auto path = wil::make_cotaskmem_string_nothrow(nullptr, size);
|
||||||
|
|
||||||
|
DWORD copied = processHandle ?
|
||||||
|
::GetModuleFileNameExW(processHandle, moduleHandle, path.get(), size) :
|
||||||
|
::GetModuleFileNameW(moduleHandle, path.get(), size);
|
||||||
|
REQUIRE(copied < size);
|
||||||
|
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST_CASE("FileSystemTests::VerifyGetModuleFileNameExW", "[filesystem]")
|
TEST_CASE("FileSystemTests::VerifyGetModuleFileNameExW", "[filesystem]")
|
||||||
{
|
{
|
||||||
wil::unique_cotaskmem_string path;
|
wil::unique_cotaskmem_string path;
|
||||||
|
@ -455,6 +575,58 @@ TEST_CASE("FileSystemTests::VerifyGetModuleFileNameExW", "[filesystem]")
|
||||||
REQUIRE(wcscmp(path.get(), path2.get()) == 0);
|
REQUIRE(wcscmp(path.get(), path2.get()) == 0);
|
||||||
|
|
||||||
REQUIRE_FAILED(wil::GetModuleFileNameExW(nullptr, (HMODULE)INVALID_HANDLE_VALUE, path));
|
REQUIRE_FAILED(wil::GetModuleFileNameExW(nullptr, (HMODULE)INVALID_HANDLE_VALUE, path));
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
auto wstringPath = wil::GetModuleFileNameExW<std::wstring, 15>(nullptr, nullptr);
|
||||||
|
REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
|
||||||
|
REQUIRE(wstringPath == NativeGetModuleFileNameWrap(nullptr, nullptr).get());
|
||||||
|
|
||||||
|
wstringPath = wil::GetModuleFileNameExW<std::wstring, 15>(GetCurrentProcess(), nullptr);
|
||||||
|
REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
|
||||||
|
REQUIRE(wstringPath == NativeGetModuleFileNameWrap(GetCurrentProcess(), nullptr).get());
|
||||||
|
|
||||||
|
wstringPath = wil::GetModuleFileNameW<std::wstring, 15>(nullptr);
|
||||||
|
REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
|
||||||
|
REQUIRE(wstringPath == NativeGetModuleFileNameWrap(nullptr, nullptr).get());
|
||||||
|
|
||||||
|
HMODULE kernel32 = ::GetModuleHandleW(L"kernel32.dll");
|
||||||
|
|
||||||
|
wstringPath = wil::GetModuleFileNameExW<std::wstring, 15>(nullptr, kernel32);
|
||||||
|
REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
|
||||||
|
REQUIRE(wstringPath == NativeGetModuleFileNameWrap(nullptr, kernel32).get());
|
||||||
|
|
||||||
|
wstringPath = wil::GetModuleFileNameExW<std::wstring, 15>(GetCurrentProcess(), kernel32);
|
||||||
|
REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
|
||||||
|
REQUIRE(wstringPath == NativeGetModuleFileNameWrap(GetCurrentProcess(), kernel32).get());
|
||||||
|
|
||||||
|
wstringPath = wil::GetModuleFileNameW<std::wstring, 15>(kernel32);
|
||||||
|
REQUIRE(wstringPath.length() == ::wcslen(wstringPath.c_str()));
|
||||||
|
REQUIRE(wstringPath == NativeGetModuleFileNameWrap(nullptr, kernel32).get());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("FileSystemTests::QueryFullProcessImageNameW and GetModuleFileNameW", "[filesystem]")
|
||||||
|
{
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
auto procName = wil::QueryFullProcessImageNameW<std::wstring>();
|
||||||
|
auto moduleName = wil::GetModuleFileNameW<std::wstring>();
|
||||||
|
REQUIRE(procName == moduleName);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("FileSystemTests::QueryFullProcessImageNameW", "[filesystem]")
|
||||||
|
{
|
||||||
|
WCHAR fullName[MAX_PATH * 4];
|
||||||
|
DWORD fullNameSize = ARRAYSIZE(fullName);
|
||||||
|
REQUIRE(::QueryFullProcessImageNameW(::GetCurrentProcess(), 0, fullName, &fullNameSize));
|
||||||
|
|
||||||
|
wil::unique_cotaskmem_string path;
|
||||||
|
REQUIRE_SUCCEEDED(wil::QueryFullProcessImageNameW(::GetCurrentProcess(), 0, path));
|
||||||
|
REQUIRE(wcscmp(fullName, path.get()) == 0);
|
||||||
|
|
||||||
|
wil::unique_cotaskmem nativePath;
|
||||||
|
REQUIRE_SUCCEEDED((wil::QueryFullProcessImageNameW<wil::unique_cotaskmem_string, 15>(::GetCurrentProcess(), 0, path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
|
||||||
|
#include <wil/result.h>
|
||||||
|
#include <wil/nt_result_macros.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#define STATUS_OBJECT_PATH_NOT_FOUND ((NTSTATUS)0xC000003AL)
|
||||||
|
#define STATUS_INTERNAL_ERROR ((NTSTATUS)0xC00000E5L)
|
||||||
|
#define STATUS_INVALID_CONNECTION ((NTSTATUS)0xC0000140L)
|
||||||
|
#define E_LOAD_NAMESERVICE_FAILED ((HRESULT)0x80000140L)
|
||||||
|
|
||||||
|
TEST_CASE("NtResultTests::NtReturn", "[result]")
|
||||||
|
{
|
||||||
|
auto status = []()
|
||||||
|
{
|
||||||
|
NT_RETURN_NTSTATUS(STATUS_INVALID_CONNECTION);
|
||||||
|
}();
|
||||||
|
REQUIRE(status == STATUS_INVALID_CONNECTION);
|
||||||
|
|
||||||
|
status = []()
|
||||||
|
{
|
||||||
|
NT_RETURN_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Test NT_RETURN_NTSTATUS_MSG");
|
||||||
|
}();
|
||||||
|
REQUIRE(status == STATUS_INVALID_CONNECTION);
|
||||||
|
|
||||||
|
status = []()
|
||||||
|
{
|
||||||
|
NT_RETURN_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Test NT_RETURN_NTSTATUS_MSG %s", L"with parameter");
|
||||||
|
}();
|
||||||
|
REQUIRE(status == STATUS_INVALID_CONNECTION);
|
||||||
|
|
||||||
|
status = []()
|
||||||
|
{
|
||||||
|
NT_RETURN_IF_NTSTATUS_FAILED(STATUS_INVALID_CONNECTION);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}();
|
||||||
|
REQUIRE(status == STATUS_INVALID_CONNECTION);
|
||||||
|
|
||||||
|
status = []()
|
||||||
|
{
|
||||||
|
NT_RETURN_IF_NTSTATUS_FAILED_MSG(STATUS_INVALID_CONNECTION, "Test NT_RETURN_NTSTATUS_MSG %s", L"with parameter");
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}();
|
||||||
|
REQUIRE(status == STATUS_INVALID_CONNECTION);
|
||||||
|
|
||||||
|
status = []()
|
||||||
|
{
|
||||||
|
NT_RETURN_IF_NTSTATUS_FAILED(STATUS_SUCCESS);
|
||||||
|
return STATUS_INVALID_CONNECTION;
|
||||||
|
}();
|
||||||
|
REQUIRE(status == STATUS_INVALID_CONNECTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
TEST_CASE("NtResultTests::NtThrowCatch", "[result]")
|
||||||
|
{
|
||||||
|
// Throw NTSTATUS with immediate conversion to HRESULT. HRESULT would appear in the logs.
|
||||||
|
auto hr = []()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
THROW_NTSTATUS(STATUS_INVALID_CONNECTION);
|
||||||
|
}
|
||||||
|
CATCH_RETURN();
|
||||||
|
}();
|
||||||
|
// THROW_NTSTATUS converts NTSTATUS to HRESULT through WIN32 error code.
|
||||||
|
REQUIRE(hr == wil::details::NtStatusToHr(STATUS_INVALID_CONNECTION));
|
||||||
|
|
||||||
|
// Verify that conversion NTSTATUS -> HRESULT -> NTSTATUS is not 1:1.
|
||||||
|
auto status = []()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
THROW_HR(wil::details::NtStatusToHr(STATUS_INVALID_CONNECTION));
|
||||||
|
}
|
||||||
|
NT_CATCH_RETURN();
|
||||||
|
}();
|
||||||
|
if (wil::details::g_pfnRtlNtStatusToDosErrorNoTeb)
|
||||||
|
{
|
||||||
|
REQUIRE(status != STATUS_INVALID_CONNECTION);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
REQUIRE(status == STATUS_INVALID_CONNECTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Throw HRESULT with conversion to NTSTATUS on a best effort. NTSTATUS would appear in the logs.
|
||||||
|
status = []()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
THROW_HR(__HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND));
|
||||||
|
}
|
||||||
|
NT_CATCH_RETURN();
|
||||||
|
}();
|
||||||
|
REQUIRE(status == STATUS_OBJECT_PATH_NOT_FOUND);
|
||||||
|
|
||||||
|
// Throw HRESULT with conversion to NTSTATUS on a best effort that maps to generic error. NTSTATUS would appear in the logs.
|
||||||
|
status = []()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
THROW_HR(E_LOAD_NAMESERVICE_FAILED);
|
||||||
|
}
|
||||||
|
NT_CATCH_RETURN();
|
||||||
|
}();
|
||||||
|
REQUIRE(status == STATUS_INTERNAL_ERROR);
|
||||||
|
|
||||||
|
// Throw NTSTATUS without conversion. NTSTATUS would appear in the logs.
|
||||||
|
status = []()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
THROW_NTSTATUS(STATUS_INVALID_CONNECTION);
|
||||||
|
}
|
||||||
|
NT_CATCH_RETURN();
|
||||||
|
}();
|
||||||
|
REQUIRE(status == STATUS_INVALID_CONNECTION);
|
||||||
|
|
||||||
|
status = []()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
THROW_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Throw STATUS_INVALID_CONNECTION as NTSTATUS");
|
||||||
|
}
|
||||||
|
NT_CATCH_RETURN();
|
||||||
|
}();
|
||||||
|
REQUIRE(status == STATUS_INVALID_CONNECTION);
|
||||||
|
|
||||||
|
status = []()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
THROW_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Throw STATUS_INVALID_CONNECTION as NTSTATUS with custom catch");
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
LOG_CAUGHT_EXCEPTION();
|
||||||
|
|
||||||
|
return wil::StatusFromCaughtException();
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
REQUIRE(status == STATUS_INVALID_CONNECTION);
|
||||||
|
|
||||||
|
hr = []()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
THROW_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Throw STATUS_INVALID_CONNECTION as NTSTATUS");
|
||||||
|
}
|
||||||
|
CATCH_RETURN();
|
||||||
|
}();
|
||||||
|
REQUIRE(hr == wil::details::NtStatusToHr(STATUS_INVALID_CONNECTION));
|
||||||
|
|
||||||
|
status = []()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
THROW_NTSTATUS_MSG(STATUS_INVALID_CONNECTION, "Throw STATUS_INVALID_CONNECTION as NTSTATUS");
|
||||||
|
}
|
||||||
|
NT_CATCH_RETURN_MSG("Catching STATUS_INVALID_CONNECTION thrown by NT_THROW_NTSTATUS_MSG");
|
||||||
|
}();
|
||||||
|
REQUIRE(status == STATUS_INVALID_CONNECTION);
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -9,6 +9,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <roapi.h>
|
#include <roapi.h>
|
||||||
#include <winstring.h>
|
#include <winstring.h>
|
||||||
|
#include <WinUser.h>
|
||||||
|
|
||||||
#include <wil/resource.h>
|
#include <wil/resource.h>
|
||||||
#include <wrl/implements.h>
|
#include <wrl/implements.h>
|
||||||
|
@ -59,6 +60,14 @@ TEST_CASE("ResourceTests::TestLastErrorContext", "[resource][last_error_context]
|
||||||
SetLastError(1);
|
SetLastError(1);
|
||||||
}
|
}
|
||||||
REQUIRE(GetLastError() == 1);
|
REQUIRE(GetLastError() == 1);
|
||||||
|
|
||||||
|
// The value in the context is unimpacted by other things changing the last error
|
||||||
|
{
|
||||||
|
SetLastError(42);
|
||||||
|
auto error42 = wil::last_error_context();
|
||||||
|
SetLastError(1);
|
||||||
|
REQUIRE(error42.value() == 42);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("ResourceTests::TestScopeExit", "[resource][scope_exit]")
|
TEST_CASE("ResourceTests::TestScopeExit", "[resource][scope_exit]")
|
||||||
|
@ -267,6 +276,40 @@ void UniqueProcessInfo()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Compilation only test...
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
void NoexceptConstructibleTest()
|
||||||
|
{
|
||||||
|
using BaseStorage = wil::details::unique_storage<wil::details::handle_resource_policy>;
|
||||||
|
|
||||||
|
struct ThrowingConstructor : BaseStorage
|
||||||
|
{
|
||||||
|
ThrowingConstructor() = default;
|
||||||
|
explicit ThrowingConstructor(HANDLE) __WI_NOEXCEPT_(false) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ProtectedConstructor : BaseStorage
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
ProtectedConstructor() = default;
|
||||||
|
explicit ProtectedConstructor(HANDLE) WI_NOEXCEPT {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// wil::unique_handle is one of the many types which are expected to be noexcept
|
||||||
|
// constructible since they don't perform any "advanced" initialization.
|
||||||
|
static_assert(wistd::is_nothrow_default_constructible_v<wil::unique_handle>, "wil::unique_any_t should always be nothrow default constructible");
|
||||||
|
static_assert(wistd::is_nothrow_constructible_v<wil::unique_handle, HANDLE>, "wil::unique_any_t should be noexcept if the storage is");
|
||||||
|
|
||||||
|
// The inverse: A throwing storage constructor.
|
||||||
|
static_assert(wistd::is_nothrow_default_constructible_v<wil::unique_any_t<ThrowingConstructor>>, "wil::unique_any_t should always be nothrow default constructible");
|
||||||
|
static_assert(!wistd::is_nothrow_constructible_v<wil::unique_any_t<ThrowingConstructor>, HANDLE>, "wil::unique_any_t shouldn't be noexcept if the storage isn't");
|
||||||
|
|
||||||
|
// With a protected constructor wil::unique_any_t will be unable to correctly
|
||||||
|
// "forward" the noexcept attribute, but the code should still compile.
|
||||||
|
wil::unique_any_t<ProtectedConstructor> p{ INVALID_HANDLE_VALUE };
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
struct FakeComInterface
|
struct FakeComInterface
|
||||||
{
|
{
|
||||||
void AddRef()
|
void AddRef()
|
||||||
|
@ -732,4 +775,90 @@ TEST_CASE("DefaultTemplateParamCompiles", "[resource]")
|
||||||
|
|
||||||
wil::unique_midl_ptr<> g;
|
wil::unique_midl_ptr<> g;
|
||||||
wil::unique_cotaskmem_ptr<> h;
|
wil::unique_cotaskmem_ptr<> h;
|
||||||
|
wil::unique_mapview_ptr<> i;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("UniqueInvokeCleanupMembers", "[resource]")
|
||||||
|
{
|
||||||
|
// Case 1 - unique_ptr<> for a T* that has a "destroy" member
|
||||||
|
struct ThingWithDestroy
|
||||||
|
{
|
||||||
|
bool destroyed = false;
|
||||||
|
void destroy() { destroyed = true; };
|
||||||
|
};
|
||||||
|
ThingWithDestroy toDestroy;
|
||||||
|
wil::unique_any<ThingWithDestroy*, decltype(&ThingWithDestroy::destroy), &ThingWithDestroy::destroy> p(&toDestroy);
|
||||||
|
p.reset();
|
||||||
|
REQUIRE(!p);
|
||||||
|
REQUIRE(toDestroy.destroyed);
|
||||||
|
|
||||||
|
// Case 2 - unique_struct calling a member, like above
|
||||||
|
struct ThingToDestroy2
|
||||||
|
{
|
||||||
|
bool* destroyed;
|
||||||
|
void destroy() { *destroyed = true; };
|
||||||
|
};
|
||||||
|
bool structDestroyed = false;
|
||||||
|
{
|
||||||
|
wil::unique_struct<ThingToDestroy2, decltype(&ThingToDestroy2::destroy), &ThingToDestroy2::destroy> other;
|
||||||
|
other.destroyed = &structDestroyed;
|
||||||
|
REQUIRE(!structDestroyed);
|
||||||
|
}
|
||||||
|
REQUIRE(structDestroyed);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ITokenTester : IUnknown
|
||||||
|
{
|
||||||
|
virtual void DirectClose(DWORD_PTR token) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TokenTester : ITokenTester
|
||||||
|
{
|
||||||
|
IFACEMETHOD_(ULONG, AddRef)() override { return 2; }
|
||||||
|
IFACEMETHOD_(ULONG, Release)() override { return 1; }
|
||||||
|
IFACEMETHOD(QueryInterface)(REFIID, void**) { return E_NOINTERFACE; }
|
||||||
|
void DirectClose(DWORD_PTR token) override {
|
||||||
|
m_closed = (token == m_closeToken);
|
||||||
|
}
|
||||||
|
bool m_closed = false;
|
||||||
|
DWORD_PTR m_closeToken;
|
||||||
|
};
|
||||||
|
|
||||||
|
void MyTokenTesterCloser(ITokenTester* tt, DWORD_PTR token)
|
||||||
|
{
|
||||||
|
tt->DirectClose(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ComTokenCloser", "[resource]")
|
||||||
|
{
|
||||||
|
using token_tester_t = wil::unique_com_token<ITokenTester, DWORD_PTR, decltype(MyTokenTesterCloser), &MyTokenTesterCloser>;
|
||||||
|
|
||||||
|
TokenTester tt;
|
||||||
|
tt.m_closeToken = 4;
|
||||||
|
{
|
||||||
|
token_tester_t tmp{ &tt, 4 };
|
||||||
|
}
|
||||||
|
REQUIRE(tt.m_closed);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("ComTokenDirectCloser", "[resource]")
|
||||||
|
{
|
||||||
|
using token_tester_t = wil::unique_com_token<ITokenTester, DWORD_PTR, decltype(&ITokenTester::DirectClose), &ITokenTester::DirectClose>;
|
||||||
|
|
||||||
|
TokenTester tt;
|
||||||
|
tt.m_closeToken = 4;
|
||||||
|
{
|
||||||
|
token_tester_t tmp{ &tt, 4 };
|
||||||
|
}
|
||||||
|
REQUIRE(tt.m_closed);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("UniqueCloseClipboardCall", "[resource]")
|
||||||
|
{
|
||||||
|
#if defined(__WIL__WINUSER_) && !defined(NOCLIPBOARD)
|
||||||
|
if (auto clip = wil::open_clipboard(nullptr))
|
||||||
|
{
|
||||||
|
REQUIRE(::EmptyClipboard());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
|
||||||
#include <wil/com.h>
|
#include <wil/com.h>
|
||||||
#include <wil/result.h>
|
#include <wil/result.h>
|
||||||
|
|
||||||
|
#if (NTDDI_VERSION >= NTDDI_WIN8)
|
||||||
#include <wil/result_originate.h>
|
#include <wil/result_originate.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <roerrorapi.h>
|
#include <roerrorapi.h>
|
||||||
|
|
||||||
|
@ -323,7 +326,7 @@ TEST_CASE("ResultTests::ExceptionHandling", "[result]")
|
||||||
RETURN_CAUGHT_EXCEPTION_EXPECTED();
|
RETURN_CAUGHT_EXCEPTION_EXPECTED();
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
REQUIRE(failures.size() == 0);
|
REQUIRE(failures.empty());
|
||||||
REQUIRE(hr == E_OUTOFMEMORY);
|
REQUIRE(hr == E_OUTOFMEMORY);
|
||||||
}
|
}
|
||||||
failures.clear();
|
failures.clear();
|
||||||
|
@ -339,7 +342,7 @@ TEST_CASE("ResultTests::ExceptionHandling", "[result]")
|
||||||
{
|
{
|
||||||
throw std::bad_alloc();
|
throw std::bad_alloc();
|
||||||
});
|
});
|
||||||
REQUIRE(failures.size() == 0);
|
REQUIRE(failures.empty());
|
||||||
REQUIRE(hr == E_OUTOFMEMORY);
|
REQUIRE(hr == E_OUTOFMEMORY);
|
||||||
}
|
}
|
||||||
failures.clear();
|
failures.clear();
|
||||||
|
@ -436,16 +439,16 @@ void ExceptionHandlingCompilationTest()
|
||||||
try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION(); } } catch (...) {}
|
try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION(); } } catch (...) {}
|
||||||
try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION_MSG("train: %d", 42); } } catch (...) {}
|
try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION_MSG("train: %d", 42); } } catch (...) {}
|
||||||
|
|
||||||
HRESULT hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::All, [&]
|
wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::All, [&]
|
||||||
{
|
{
|
||||||
THROW_HR(E_FAIL);
|
THROW_HR(E_FAIL);
|
||||||
});
|
});
|
||||||
|
|
||||||
hr = wil::ResultFromException(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::None, [&]
|
wil::ResultFromException(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::None, [&]
|
||||||
{
|
{
|
||||||
});
|
});
|
||||||
|
|
||||||
hr = wil::ResultFromException([&]
|
wil::ResultFromException([&]
|
||||||
{
|
{
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -479,7 +482,7 @@ TEST_CASE("ResultTests::ErrorMacros", "[result]")
|
||||||
}
|
}
|
||||||
|
|
||||||
// The originate helper isn't compatible with CX so don't test it in that mode.
|
// The originate helper isn't compatible with CX so don't test it in that mode.
|
||||||
#ifndef __cplusplus_winrt
|
#if !defined(__cplusplus_winrt) && (NTDDI_VERSION >= NTDDI_WIN8)
|
||||||
TEST_CASE("ResultTests::NoOriginationByDefault", "[result]")
|
TEST_CASE("ResultTests::NoOriginationByDefault", "[result]")
|
||||||
{
|
{
|
||||||
::wil::SetOriginateErrorCallback(nullptr);
|
::wil::SetOriginateErrorCallback(nullptr);
|
||||||
|
@ -572,4 +575,17 @@ TEST_CASE("ResultTests::AutomaticOriginationOnFailure", "[result]")
|
||||||
}();
|
}();
|
||||||
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
|
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
|
||||||
}
|
}
|
||||||
#endif // __cplusplus_winrt
|
#endif
|
||||||
|
|
||||||
|
TEST_CASE("ResultTests::ReportDoesNotChangeLastError", "[result]")
|
||||||
|
{
|
||||||
|
decltype(wil::details::g_pfnLoggingCallback) oopsie = [](wil::FailureInfo const&) noexcept
|
||||||
|
{
|
||||||
|
::SetLastError(ERROR_ABANDON_HIBERFILE);
|
||||||
|
};
|
||||||
|
auto swap = witest::AssignTemporaryValue(&wil::details::g_pfnLoggingCallback, oopsie);
|
||||||
|
|
||||||
|
::SetLastError(ERROR_ABIOS_ERROR);
|
||||||
|
LOG_IF_WIN32_BOOL_FALSE(FALSE);
|
||||||
|
REQUIRE(::GetLastError() == ERROR_ABIOS_ERROR);
|
||||||
|
}
|
||||||
|
|
|
@ -11,8 +11,8 @@ void RpcMethodReturnsVoid(ULONG toRaise)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FOO_CONTEXT_T {};
|
struct FOO_CONTEXT_T {};
|
||||||
typedef FOO_CONTEXT_T* FOO_CONTEXT;
|
using FOO_CONTEXT = FOO_CONTEXT_T*;
|
||||||
typedef FOO_CONTEXT* PFOO_CONTEXT;
|
using PFOO_CONTEXT = FOO_CONTEXT*;
|
||||||
|
|
||||||
void CloseContextHandle(_Inout_ PFOO_CONTEXT)
|
void CloseContextHandle(_Inout_ PFOO_CONTEXT)
|
||||||
{
|
{
|
||||||
|
|
|
@ -45,3 +45,140 @@ TEST_CASE("StlTests::TestSecureAllocator", "[stl][secure_allocator]")
|
||||||
wil::secure_vector<dummy> sensitiveBytes(32, dummy{ 'a' });
|
wil::secure_vector<dummy> sensitiveBytes(32, dummy{ 'a' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if __WI_LIBCPP_STD_VER >= 17
|
||||||
|
|
||||||
|
struct CustomNoncopyableString
|
||||||
|
{
|
||||||
|
CustomNoncopyableString() = default;
|
||||||
|
CustomNoncopyableString(const CustomNoncopyableString&) = delete;
|
||||||
|
void operator=(const CustomNoncopyableString&) = delete;
|
||||||
|
|
||||||
|
constexpr operator PCSTR() const { return "hello"; }
|
||||||
|
constexpr operator PCWSTR() const { return L"w-hello"; }
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_CASE("StlTests::TestZStringView", "[stl][zstring_view]")
|
||||||
|
{
|
||||||
|
// Test empty cases
|
||||||
|
REQUIRE(wil::zstring_view{}.length() == (size_t)0u);
|
||||||
|
REQUIRE(wil::zstring_view{}.data() == nullptr);
|
||||||
|
REQUIRE(wil::zstring_view{}.c_str() == nullptr);
|
||||||
|
|
||||||
|
// Test empty string cases
|
||||||
|
REQUIRE(wil::zstring_view{""}[0] == '\0');
|
||||||
|
REQUIRE(wil::zstring_view{""}.c_str()[0] == '\0');
|
||||||
|
REQUIRE(wil::zstring_view{""}.length() == 0);
|
||||||
|
|
||||||
|
// Test different constructor equality
|
||||||
|
constexpr wil::zstring_view fromLiteral = "abc";
|
||||||
|
REQUIRE(fromLiteral.length() == strlen("abc"));
|
||||||
|
|
||||||
|
std::string stlString = "abc";
|
||||||
|
wil::zstring_view fromString(stlString);
|
||||||
|
wil::zstring_view fromPtr(stlString.data());
|
||||||
|
|
||||||
|
static constexpr char charArray[] = "abc";
|
||||||
|
constexpr wil::zstring_view fromArray(charArray);
|
||||||
|
|
||||||
|
static constexpr char extendedCharArray[] = "abc\0\0\0\0\0";
|
||||||
|
constexpr wil::zstring_view fromExtendedArray(extendedCharArray);
|
||||||
|
|
||||||
|
wil::zstring_view copy = fromLiteral;
|
||||||
|
|
||||||
|
REQUIRE(fromLiteral == stlString);
|
||||||
|
REQUIRE(fromLiteral == fromString);
|
||||||
|
REQUIRE(fromLiteral == fromArray);
|
||||||
|
REQUIRE(fromLiteral == fromExtendedArray);
|
||||||
|
REQUIRE(fromLiteral == copy);
|
||||||
|
|
||||||
|
// Test decay to std::string_view
|
||||||
|
std::string_view sv = fromLiteral;
|
||||||
|
REQUIRE(sv == fromLiteral);
|
||||||
|
|
||||||
|
// Test operator[]
|
||||||
|
REQUIRE(fromLiteral[0] == 'a');
|
||||||
|
REQUIRE(fromLiteral[1] == 'b');
|
||||||
|
REQUIRE(fromLiteral[2] == 'c');
|
||||||
|
REQUIRE(fromLiteral[3] == '\0');
|
||||||
|
|
||||||
|
// Test constructing with no NULL in range
|
||||||
|
static constexpr char badCharArray[2][3] = {{'a', 'b', 'c' }, {'a', 'b', 'c' }};
|
||||||
|
REQUIRE_CRASH((wil::zstring_view{ &badCharArray[0][0], _countof(badCharArray[0]) }));
|
||||||
|
REQUIRE_CRASH((wil::zstring_view{ badCharArray[0] }));
|
||||||
|
|
||||||
|
// Test constructing with a NULL one character past the valid range, guarding against off-by-one errors
|
||||||
|
// Overloads taking an explicit length trust the user that they ensure valid memory follows the buffer
|
||||||
|
static constexpr char badCharArrayOffByOne[2][3] = {{'a', 'b', 'c' }, {}};
|
||||||
|
const wil::zstring_view fromTerminatedCharArray(&badCharArrayOffByOne[0][0], _countof(badCharArrayOffByOne[0]));
|
||||||
|
REQUIRE(fromLiteral == fromTerminatedCharArray);
|
||||||
|
REQUIRE_CRASH((wil::zstring_view{ badCharArrayOffByOne[0] }));
|
||||||
|
|
||||||
|
// Test constructing from custom string type
|
||||||
|
CustomNoncopyableString customString;
|
||||||
|
wil::zstring_view fromCustomString(customString);
|
||||||
|
REQUIRE(fromCustomString == (PCSTR)customString);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("StlTests::TestZWStringView", "[stl][zstring_view]")
|
||||||
|
{
|
||||||
|
// Test empty cases
|
||||||
|
REQUIRE(wil::zwstring_view{}.length() == (size_t)0u);
|
||||||
|
REQUIRE(wil::zwstring_view{}.data() == nullptr);
|
||||||
|
REQUIRE(wil::zwstring_view{}.c_str() == nullptr);
|
||||||
|
|
||||||
|
// Test empty string cases
|
||||||
|
REQUIRE(wil::zwstring_view{L""}[0] == L'\0');
|
||||||
|
REQUIRE(wil::zwstring_view{L""}.c_str()[0] == L'\0');
|
||||||
|
REQUIRE(wil::zwstring_view{L""}.length() == 0);
|
||||||
|
|
||||||
|
// Test different constructor equality
|
||||||
|
constexpr wil::zwstring_view fromLiteral = L"abc";
|
||||||
|
REQUIRE(fromLiteral.length() == wcslen(L"abc"));
|
||||||
|
|
||||||
|
std::wstring stlString = L"abc";
|
||||||
|
wil::zwstring_view fromString(stlString);
|
||||||
|
wil::zwstring_view fromPtr(stlString.data());
|
||||||
|
|
||||||
|
static constexpr wchar_t charArray[] = L"abc";
|
||||||
|
constexpr wil::zwstring_view fromArray(charArray);
|
||||||
|
|
||||||
|
static constexpr wchar_t extendedCharArray[] = L"abc\0\0\0\0\0";
|
||||||
|
constexpr wil::zwstring_view fromExtendedArray(extendedCharArray);
|
||||||
|
|
||||||
|
wil::zwstring_view copy = fromLiteral;
|
||||||
|
|
||||||
|
REQUIRE(fromLiteral == stlString);
|
||||||
|
REQUIRE(fromLiteral == fromString);
|
||||||
|
REQUIRE(fromLiteral == fromArray);
|
||||||
|
REQUIRE(fromLiteral == fromExtendedArray);
|
||||||
|
REQUIRE(fromLiteral == copy);
|
||||||
|
|
||||||
|
// Test decay to std::wstring_view
|
||||||
|
std::wstring_view sv = fromLiteral;
|
||||||
|
REQUIRE(sv == fromLiteral);
|
||||||
|
|
||||||
|
// Test operator[]
|
||||||
|
REQUIRE(fromLiteral[0] == L'a');
|
||||||
|
REQUIRE(fromLiteral[1] == L'b');
|
||||||
|
REQUIRE(fromLiteral[2] == L'c');
|
||||||
|
REQUIRE(fromLiteral[3] == L'\0');
|
||||||
|
|
||||||
|
// Test constructing with no NULL in range
|
||||||
|
static constexpr wchar_t badCharArray[2][3] = {{ L'a', L'b', L'c' }, { L'a', L'b', L'c' } };
|
||||||
|
REQUIRE_CRASH((wil::zwstring_view{ &badCharArray[0][0], _countof(badCharArray[0]) }));
|
||||||
|
REQUIRE_CRASH((wil::zwstring_view{ badCharArray[0] }));
|
||||||
|
|
||||||
|
// Test constructing with a NULL one character past the valid range, guarding against off-by-one errors
|
||||||
|
// Overloads taking an explicit length trust the user that they ensure valid memory follows the buffer
|
||||||
|
static constexpr wchar_t badCharArrayOffByOne[2][3] = {{ L'a', L'b', L'c' }, {}};
|
||||||
|
const wil::zwstring_view fromTerminatedCharArray(&badCharArrayOffByOne[0][0], _countof(badCharArrayOffByOne[0]));
|
||||||
|
REQUIRE(fromLiteral == fromTerminatedCharArray);
|
||||||
|
REQUIRE_CRASH((wil::zwstring_view{ badCharArrayOffByOne[0] }));
|
||||||
|
|
||||||
|
// Test constructing from custom string type
|
||||||
|
CustomNoncopyableString customString;
|
||||||
|
wil::zwstring_view fromCustomString(customString);
|
||||||
|
REQUIRE(fromCustomString == (PCWSTR)customString);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -38,6 +38,7 @@ TEST_CASE("TokenHelpersTests::VerifyOpenCurrentAccessToken", "[token_helpers]")
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||||
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationNoThrow", "[token_helpers]")
|
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationNoThrow", "[token_helpers]")
|
||||||
{
|
{
|
||||||
SECTION("Passing a null token")
|
SECTION("Passing a null token")
|
||||||
|
@ -92,6 +93,7 @@ TEST_CASE("TokenHelpersTests::VerifyLinkedToken", "[token_helpers]")
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
bool IsImpersonating()
|
bool IsImpersonating()
|
||||||
{
|
{
|
||||||
|
@ -186,6 +188,7 @@ TEST_CASE("TokenHelpersTests::VerifyResetThreadToken", "[token_helpers]")
|
||||||
}
|
}
|
||||||
#endif // WIL_ENABLE_EXCEPTIONS
|
#endif // WIL_ENABLE_EXCEPTIONS
|
||||||
|
|
||||||
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||||
template <typename T, wistd::enable_if_t<!wil::details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
|
template <typename T, wistd::enable_if_t<!wil::details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
|
||||||
void TestGetTokenInfoForCurrentThread()
|
void TestGetTokenInfoForCurrentThread()
|
||||||
{
|
{
|
||||||
|
@ -251,6 +254,7 @@ TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationSecurityImpersonationLeve
|
||||||
|
|
||||||
RevertToSelf();
|
RevertToSelf();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool operator==(const SID_IDENTIFIER_AUTHORITY& left, const SID_IDENTIFIER_AUTHORITY& right)
|
bool operator==(const SID_IDENTIFIER_AUTHORITY& left, const SID_IDENTIFIER_AUTHORITY& right)
|
||||||
{
|
{
|
||||||
|
@ -274,6 +278,7 @@ TEST_CASE("TokenHelpersTests::StaticSid", "[token_helpers]")
|
||||||
REQUIRE(*GetSidSubAuthority(staticSid.get(), 1) == DOMAIN_ALIAS_RID_GUESTS);
|
REQUIRE(*GetSidSubAuthority(staticSid.get(), 1) == DOMAIN_ALIAS_RID_GUESTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||||
TEST_CASE("TokenHelpersTests::TestMembership", "[token_helpers]")
|
TEST_CASE("TokenHelpersTests::TestMembership", "[token_helpers]")
|
||||||
{
|
{
|
||||||
bool member;
|
bool member;
|
||||||
|
@ -318,3 +323,4 @@ TEST_CASE("TokenHelpersTests::Verify_impersonate_token", "[token_helpers]")
|
||||||
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_TYPE>());
|
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_TYPE>());
|
||||||
}
|
}
|
||||||
#endif // WIL_ENABLE_EXCEPTIONS
|
#endif // WIL_ENABLE_EXCEPTIONS
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
#include <wil/Tracelogging.h>
|
||||||
|
|
||||||
|
// Just verify that Tracelogging.h compiles.
|
|
@ -91,7 +91,7 @@ TEST_CASE("EventWatcherTests::VerifyDelivery", "[resource][event_watcher]")
|
||||||
int volatile countObserved = 0;
|
int volatile countObserved = 0;
|
||||||
auto watcher = wil::make_event_watcher_nothrow([&]
|
auto watcher = wil::make_event_watcher_nothrow([&]
|
||||||
{
|
{
|
||||||
countObserved++;
|
countObserved = countObserved + 1;
|
||||||
notificationReceived.SetEvent();
|
notificationReceived.SetEvent();
|
||||||
});
|
});
|
||||||
REQUIRE(watcher != nullptr);
|
REQUIRE(watcher != nullptr);
|
||||||
|
@ -125,7 +125,7 @@ TEST_CASE("EventWatcherTests::VerifyLastChangeObserved", "[resource][event_watch
|
||||||
auto watcher = wil::make_event_watcher_nothrow(make_event(eventOption), [&]
|
auto watcher = wil::make_event_watcher_nothrow(make_event(eventOption), [&]
|
||||||
{
|
{
|
||||||
allChangesMade.wait();
|
allChangesMade.wait();
|
||||||
countObserved++;
|
countObserved = countObserved + 1;
|
||||||
lastObservedState = stateToObserve;
|
lastObservedState = stateToObserve;
|
||||||
processedChange.SetEvent();
|
processedChange.SetEvent();
|
||||||
});
|
});
|
||||||
|
@ -213,7 +213,7 @@ TEST_CASE("RegistryWatcherTests::VerifyDelivery", "[registry][registry_watcher]"
|
||||||
auto volatile observedChangeType = wil::RegistryChangeKind::Delete;
|
auto volatile observedChangeType = wil::RegistryChangeKind::Delete;
|
||||||
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
|
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
|
||||||
{
|
{
|
||||||
countObserved++;
|
countObserved = countObserved + 1;
|
||||||
observedChangeType = changeType;
|
observedChangeType = changeType;
|
||||||
notificationReceived.SetEvent();
|
notificationReceived.SetEvent();
|
||||||
});
|
});
|
||||||
|
@ -252,7 +252,7 @@ TEST_CASE("RegistryWatcherTests::VerifyLastChangeObserved", "[registry][registry
|
||||||
called = true;
|
called = true;
|
||||||
|
|
||||||
allChangesMade.wait();
|
allChangesMade.wait();
|
||||||
countObserved++;
|
countObserved = countObserved + 1;
|
||||||
lastObservedState = stateToObserve;
|
lastObservedState = stateToObserve;
|
||||||
DWORD value, cbValue = sizeof(value);
|
DWORD value, cbValue = sizeof(value);
|
||||||
RegGetValueW(ROOT_KEY_PAIR, L"value", RRF_RT_REG_DWORD, nullptr, &value, &cbValue);
|
RegGetValueW(ROOT_KEY_PAIR, L"value", RRF_RT_REG_DWORD, nullptr, &value, &cbValue);
|
||||||
|
@ -287,7 +287,7 @@ TEST_CASE("RegistryWatcherTests::VerifyDeleteBehavior", "[registry][registry_wat
|
||||||
auto volatile observedChangeType = wil::RegistryChangeKind::Modify;
|
auto volatile observedChangeType = wil::RegistryChangeKind::Modify;
|
||||||
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
|
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
|
||||||
{
|
{
|
||||||
countObserved++;
|
countObserved = countObserved + 1;
|
||||||
observedChangeType = changeType;
|
observedChangeType = changeType;
|
||||||
notificationReceived.SetEvent();
|
notificationReceived.SetEvent();
|
||||||
});
|
});
|
||||||
|
@ -318,7 +318,7 @@ TEST_CASE("RegistryWatcherTests::VerifyResetInCallback", "[registry][registry_wa
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stress test, disabled by default
|
// Stress test, disabled by default
|
||||||
TEST_CASE("RegistryWatcherTests::VerifyResetInCallbackStress", "[!hide][registry][registry_watcher][stress]")
|
TEST_CASE("RegistryWatcherTests::VerifyResetInCallbackStress", "[LocalOnly][registry][registry_watcher][stress]")
|
||||||
{
|
{
|
||||||
for (DWORD value = 0; value < 10000; ++value)
|
for (DWORD value = 0; value < 10000; ++value)
|
||||||
{
|
{
|
||||||
|
@ -355,12 +355,12 @@ TEST_CASE("RegistryWatcherTests::VerifyResetAfterDelete", "[registry][registry_w
|
||||||
auto volatile observedChangeType = wil::RegistryChangeKind::Modify;
|
auto volatile observedChangeType = wil::RegistryChangeKind::Modify;
|
||||||
wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
|
wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
|
||||||
{
|
{
|
||||||
countObserved++;
|
countObserved = countObserved + 1;
|
||||||
observedChangeType = changeType;
|
observedChangeType = changeType;
|
||||||
notificationReceived.SetEvent();
|
notificationReceived.SetEvent();
|
||||||
watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
|
watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
|
||||||
{
|
{
|
||||||
countObserved++;
|
countObserved = countObserved + 1;
|
||||||
observedChangeType = changeType;
|
observedChangeType = changeType;
|
||||||
notificationReceived.SetEvent();
|
notificationReceived.SetEvent();
|
||||||
});
|
});
|
||||||
|
@ -394,7 +394,7 @@ TEST_CASE("RegistryWatcherTests::VerifyCallbackFinishesBeforeFreed", "[registry]
|
||||||
notificationReceived.SetEvent();
|
notificationReceived.SetEvent();
|
||||||
// ensure that the callback is still being executed while the watcher is reset().
|
// ensure that the callback is still being executed while the watcher is reset().
|
||||||
deleteNotification.wait(200);
|
deleteNotification.wait(200);
|
||||||
deleteObserved++;
|
deleteObserved = deleteObserved + 1;
|
||||||
notificationReceived.SetEvent();
|
notificationReceived.SetEvent();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
|
||||||
#include <time.h> // TODO: https://github.com/microsoft/wil/issues/44
|
|
||||||
#include <wil/winrt.h>
|
#include <wil/winrt.h>
|
||||||
|
|
||||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Required for pinterface template specializations that we depend on in this test
|
// Required for pinterface template specializations that we depend on in this test
|
||||||
|
@ -240,8 +240,8 @@ void DoHStringDifferentValueComparisonTest(const wchar_t (&lhs)[LhsSize], const
|
||||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsUniqueStr, rhsUniqueStr, 1);
|
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsUniqueStr, rhsUniqueStr, 1);
|
||||||
|
|
||||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
std::wstring lhsWstr(lhs, 7);
|
std::wstring lhsWstr(lhs);
|
||||||
std::wstring rhsWstr(rhs, 7);
|
std::wstring rhsWstr(rhs);
|
||||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsWstr, 1);
|
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsWstr, 1);
|
||||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhs, 1);
|
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhs, 1);
|
||||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsNonConstArray, 1);
|
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsNonConstArray, 1);
|
||||||
|
@ -641,7 +641,7 @@ TEST_CASE("WinRTTests::TimeTTests", "[winrt][time_t]")
|
||||||
REQUIRE(time1.UniversalTime == time2.UniversalTime);
|
REQUIRE(time1.UniversalTime == time2.UniversalTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
ComPtr<IVector<IInspectable*>> MakeSampleInspectableVector()
|
ComPtr<IVector<IInspectable*>> MakeSampleInspectableVector(UINT32 count = 5)
|
||||||
{
|
{
|
||||||
auto result = Make<FakeVector<IInspectable*>>();
|
auto result = Make<FakeVector<IInspectable*>>();
|
||||||
REQUIRE(result);
|
REQUIRE(result);
|
||||||
|
@ -649,7 +649,7 @@ ComPtr<IVector<IInspectable*>> MakeSampleInspectableVector()
|
||||||
ComPtr<IPropertyValueStatics> propStatics;
|
ComPtr<IPropertyValueStatics> propStatics;
|
||||||
REQUIRE_SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), &propStatics));
|
REQUIRE_SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), &propStatics));
|
||||||
|
|
||||||
for (UINT32 i = 0; i < 5; ++i)
|
for (UINT32 i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
ComPtr<IInspectable> myProp;
|
ComPtr<IInspectable> myProp;
|
||||||
REQUIRE_SUCCEEDED(propStatics->CreateUInt32(i, &myProp));
|
REQUIRE_SUCCEEDED(propStatics->CreateUInt32(i, &myProp));
|
||||||
|
@ -673,12 +673,12 @@ ComPtr<IVector<HSTRING>> MakeSampleStringVector()
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
ComPtr<IVector<Point>> MakeSamplePointVector()
|
ComPtr<IVector<Point>> MakeSamplePointVector(int count = 5)
|
||||||
{
|
{
|
||||||
auto result = Make<FakeVector<Point>>();
|
auto result = Make<FakeVector<Point>>();
|
||||||
REQUIRE(result);
|
REQUIRE(result);
|
||||||
|
|
||||||
for (int i = 0; i < 5; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
auto value = static_cast<float>(i);
|
auto value = static_cast<float>(i);
|
||||||
REQUIRE_SUCCEEDED(result->Append(Point{ value, value }));
|
REQUIRE_SUCCEEDED(result->Append(Point{ value, value }));
|
||||||
|
@ -687,6 +687,33 @@ ComPtr<IVector<Point>> MakeSamplePointVector()
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T> auto cast_to(ComPtr<IInspectable> const& src)
|
||||||
|
{
|
||||||
|
ComPtr<IReference<T>> theRef;
|
||||||
|
T value{};
|
||||||
|
THROW_IF_FAILED(src.As(&theRef));
|
||||||
|
THROW_IF_FAILED(theRef->get_Value(&value));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("WinRTTests::VectorToVectorTest", "[winrt][to_vector]")
|
||||||
|
{
|
||||||
|
#if defined(WIL_ENABLE_EXCEPTIONS)
|
||||||
|
auto uninit = wil::RoInitialize_failfast();
|
||||||
|
auto ints = MakeSampleInspectableVector(100);
|
||||||
|
auto vec = wil::to_vector(ints.Get());
|
||||||
|
UINT32 size;
|
||||||
|
THROW_IF_FAILED(ints->get_Size(&size));
|
||||||
|
REQUIRE(size == vec.size());
|
||||||
|
for (UINT32 i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
ComPtr<IInspectable> oneItem;
|
||||||
|
THROW_IF_FAILED(ints->GetAt(i, &oneItem));
|
||||||
|
REQUIRE(cast_to<UINT32>(vec[i]) == cast_to<UINT32>(oneItem));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("WinRTTests::VectorRangeTest", "[winrt][vector_range]")
|
TEST_CASE("WinRTTests::VectorRangeTest", "[winrt][vector_range]")
|
||||||
{
|
{
|
||||||
auto uninit = wil::RoInitialize_failfast();
|
auto uninit = wil::RoInitialize_failfast();
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <wincrypt.h>
|
||||||
|
#include <mscat.h>
|
||||||
|
#include <softpub.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <wintrust.h>
|
||||||
|
|
||||||
|
#include <wil/resource.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
#pragma comment(lib, "Wintrust.lib")
|
||||||
|
|
||||||
|
TEST_CASE("WilWintrustWrapperTest::VerifyWintrustDataAllocateAndFree", "[resource][wintrust]")
|
||||||
|
{
|
||||||
|
wil::unique_wintrust_data uwvtData;
|
||||||
|
uwvtData.cbStruct = sizeof(WINTRUST_DATA);
|
||||||
|
DWORD zero = 0;
|
||||||
|
REQUIRE(sizeof(WINTRUST_DATA) == uwvtData.cbStruct);
|
||||||
|
|
||||||
|
uwvtData.reset();
|
||||||
|
REQUIRE(zero == uwvtData.cbStruct);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("WilWintrustWrapperTest::VerifyUniqueHCATADMINAllocateAndFree", "[resource][wintrust]")
|
||||||
|
{
|
||||||
|
wil::unique_hcatadmin hCatAdmin;
|
||||||
|
|
||||||
|
REQUIRE(
|
||||||
|
CryptCATAdminAcquireContext2(
|
||||||
|
hCatAdmin.addressof(),
|
||||||
|
nullptr,
|
||||||
|
BCRYPT_SHA256_ALGORITHM,
|
||||||
|
nullptr,
|
||||||
|
0));
|
||||||
|
|
||||||
|
REQUIRE(hCatAdmin.get() != nullptr);
|
||||||
|
hCatAdmin.reset();
|
||||||
|
REQUIRE(hCatAdmin.get() == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
|
TEST_CASE("WilWintrustWrapperTest::VerifyUnqiueHCATINFOAllocate", "[resource][wintrust]")
|
||||||
|
{
|
||||||
|
wil::shared_hcatadmin hCatAdmin;
|
||||||
|
HCATINFO hCatInfo = nullptr;
|
||||||
|
|
||||||
|
REQUIRE(
|
||||||
|
CryptCATAdminAcquireContext2(
|
||||||
|
hCatAdmin.addressof(),
|
||||||
|
nullptr,
|
||||||
|
BCRYPT_SHA256_ALGORITHM,
|
||||||
|
nullptr,
|
||||||
|
0));
|
||||||
|
|
||||||
|
wil::unique_hcatinfo hCatInfoWrapper(hCatInfo, hCatAdmin);
|
||||||
|
REQUIRE(hCatInfoWrapper.get() == nullptr);
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -1,21 +1,13 @@
|
||||||
|
|
||||||
project(witest.app)
|
|
||||||
add_executable(witest.app)
|
add_executable(witest.app)
|
||||||
|
|
||||||
add_definitions(-DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP)
|
target_compile_definitions(witest.app PRIVATE
|
||||||
|
-DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP
|
||||||
|
)
|
||||||
|
|
||||||
target_sources(witest.app PUBLIC
|
target_sources(witest.app PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
|
${COMMON_SOURCES}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
|
|
||||||
)
|
)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -204,6 +204,15 @@ namespace witest
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[noreturn]]
|
||||||
|
inline void __stdcall FakeFailfastWithContext(const wil::FailureInfo&) noexcept
|
||||||
|
{
|
||||||
|
::RaiseException(STATUS_STACK_BUFFER_OVERRUN, 0, 0, nullptr);
|
||||||
|
#ifdef __clang__
|
||||||
|
__builtin_unreachable();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
constexpr DWORD msvc_exception_code = 0xE06D7363;
|
constexpr DWORD msvc_exception_code = 0xE06D7363;
|
||||||
|
|
||||||
// This is a MAJOR hack. Catch2 registers a vectored exception handler - which gets run before our handler below -
|
// This is a MAJOR hack. Catch2 registers a vectored exception handler - which gets run before our handler below -
|
||||||
|
@ -241,6 +250,7 @@ namespace witest
|
||||||
{
|
{
|
||||||
// See above; we don't want to actually fail fast, so make sure we raise a different exception instead
|
// See above; we don't want to actually fail fast, so make sure we raise a different exception instead
|
||||||
auto restoreHandler = AssignTemporaryValue(&wil::details::g_pfnRaiseFailFastException, TranslateFailFastException);
|
auto restoreHandler = AssignTemporaryValue(&wil::details::g_pfnRaiseFailFastException, TranslateFailFastException);
|
||||||
|
auto restoreHandler2 = AssignTemporaryValue(&wil::details::g_pfnFailfastWithContextCallback, FakeFailfastWithContext);
|
||||||
|
|
||||||
auto handler = AddVectoredExceptionHandler(1, TranslateExceptionCodeHandler);
|
auto handler = AddVectoredExceptionHandler(1, TranslateExceptionCodeHandler);
|
||||||
auto removeVectoredHandler = wil::scope_exit([&] { RemoveVectoredExceptionHandler(handler); });
|
auto removeVectoredHandler = wil::scope_exit([&] { RemoveVectoredExceptionHandler(handler); });
|
||||||
|
@ -335,7 +345,7 @@ namespace witest
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && (_WIN32_WINNT >= _WIN32_WINNT_WIN7)
|
||||||
|
|
||||||
struct TestFolder
|
struct TestFolder
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,33 +1,28 @@
|
||||||
|
|
||||||
|
add_executable(witest.cpplatest)
|
||||||
|
|
||||||
# Compilers often don't use the latest C++ standard as the default. Periodically update this value (possibly conditioned
|
# Compilers often don't use the latest C++ standard as the default. Periodically update this value (possibly conditioned
|
||||||
# on compiler) as new standards are ratified/support is available
|
# on compiler) as new standards are ratified/support is available
|
||||||
if (NOT MSVC)
|
target_compile_features(witest.cpplatest PRIVATE cxx_std_20)
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
# Clang is not compatible with the experimental coroutine header, so temporarily disable some headers until full
|
||||||
|
# C++20 support is available
|
||||||
|
set(COROUTINE_SOURCES)
|
||||||
|
else()
|
||||||
|
set(COROUTINE_SOURCES
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../ComApartmentVariableTests.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(witest.cpplatest)
|
target_sources(witest.cpplatest PRIVATE
|
||||||
add_executable(witest.cpplatest)
|
${COMMON_SOURCES}
|
||||||
|
${COROUTINE_SOURCES}
|
||||||
# Semi-arbitrary insiders SDK version selected that uses C++/WinRT "2.0"
|
|
||||||
if ("${WIL_WINDOWS_SDK_VERSION}" VERSION_GREATER_EQUAL "10.0.18878.0")
|
|
||||||
target_sources(witest.cpplatest PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRT20Tests.cpp)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_sources(witest.cpplatest PUBLIC
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRTTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRTTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRT20Tests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../WinVerifyTrustTest.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,3 +6,15 @@
|
||||||
|
|
||||||
#define CATCH_CONFIG_MAIN
|
#define CATCH_CONFIG_MAIN
|
||||||
#include "catch.hpp"
|
#include "catch.hpp"
|
||||||
|
|
||||||
|
#if WITEST_ADDRESS_SANITIZER
|
||||||
|
extern "C" __declspec(dllexport)
|
||||||
|
const char* __asan_default_options()
|
||||||
|
{
|
||||||
|
return
|
||||||
|
// Tests validate OOM, so this is expected
|
||||||
|
"allocator_may_return_null=1"
|
||||||
|
// Some structs in Windows have dynamic size where we over-allocate for extra data past the end
|
||||||
|
":new_delete_type_mismatch=0";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -1,27 +1,23 @@
|
||||||
|
|
||||||
project(witest.noexcept)
|
|
||||||
add_executable(witest.noexcept)
|
add_executable(witest.noexcept)
|
||||||
|
|
||||||
# Turn off exceptions for this test
|
# Turn off exceptions for this test
|
||||||
replace_cxx_flag("/EHsc" "")
|
replace_cxx_flag("/EHsc" "/EHs-c-")
|
||||||
add_definitions(-DCATCH_CONFIG_DISABLE_EXCEPTIONS)
|
target_compile_definitions(witest.noexcept PRIVATE
|
||||||
|
-DCATCH_CONFIG_DISABLE_EXCEPTIONS
|
||||||
|
)
|
||||||
|
|
||||||
# Catch2 has a no exceptions mode (configured above), however still includes STL headers which contain try...catch
|
# Catch2 has a no exceptions mode (configured above), however still includes STL headers which contain try...catch
|
||||||
# statements... Thankfully MSVC just gives us a warning that we can disable
|
# statements... Thankfully MSVC just gives us a warning that we can disable
|
||||||
append_cxx_flag("/wd4530")
|
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||||
|
target_compile_options(witest.noexcept PRIVATE /wd4530)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_sources(witest.noexcept PUBLIC
|
target_sources(witest.noexcept PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
|
${COMMON_SOURCES}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../WinVerifyTrustTest.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp)
|
)
|
||||||
|
|
|
@ -1,21 +1,12 @@
|
||||||
|
|
||||||
project(witest)
|
|
||||||
add_executable(witest)
|
add_executable(witest)
|
||||||
|
|
||||||
target_sources(witest PUBLIC
|
target_sources(witest PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
|
${COMMON_SOURCES}
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/../WinVerifyTrustTest.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
|
||||||
|
add_executable(witest.asan)
|
||||||
|
|
||||||
|
target_compile_options(witest.asan PRIVATE -fsanitize=address)
|
||||||
|
|
||||||
|
target_compile_definitions(witest.asan PRIVATE
|
||||||
|
-DCATCH_CONFIG_NO_WINDOWS_SEH # ASAN relies on first chance AVs
|
||||||
|
-DWITEST_ADDRESS_SANITIZER # To conditionally enable/disable code
|
||||||
|
)
|
||||||
|
|
||||||
|
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||||
|
target_compile_definitions(witest.asan PRIVATE
|
||||||
|
# Not compatible with using lld-link
|
||||||
|
-D_DISABLE_VECTOR_ANNOTATION
|
||||||
|
# See below; not compatible with exceptions
|
||||||
|
-DCATCH_CONFIG_DISABLE_EXCEPTIONS
|
||||||
|
)
|
||||||
|
|
||||||
|
# Clang ASan on Windows has issues with exceptions: https://github.com/google/sanitizers/issues/749
|
||||||
|
replace_cxx_flag("/EHsc" "/EHs-c-")
|
||||||
|
|
||||||
|
if ($ENV{Platform} STREQUAL "x86")
|
||||||
|
target_link_libraries(witest.asan PRIVATE
|
||||||
|
clang_rt.asan_dynamic-i386.lib
|
||||||
|
clang_rt.asan_dynamic_runtime_thunk-i386.lib
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
target_link_libraries(witest.asan PRIVATE
|
||||||
|
clang_rt.asan_dynamic-x86_64.lib
|
||||||
|
clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
# Using exceptions, so we can compile the STL tests
|
||||||
|
set(EXTRA_SOURCES
|
||||||
|
${EXTRA_SOURCES}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_sources(witest.asan PUBLIC
|
||||||
|
${COMMON_SOURCES}
|
||||||
|
${EXTRA_SOURCES}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../WinVerifyTrustTest.cpp
|
||||||
|
)
|
|
@ -0,0 +1,25 @@
|
||||||
|
|
||||||
|
add_executable(witest.ubsan)
|
||||||
|
|
||||||
|
target_compile_options(witest.ubsan PRIVATE
|
||||||
|
-fsanitize=undefined
|
||||||
|
-fno-sanitize-recover=undefined # So we get test failures
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(witest.ubsan PRIVATE
|
||||||
|
-DWITEST_UB_SANITIZER # To conditionally enable/disable code
|
||||||
|
)
|
||||||
|
|
||||||
|
# UBSan libraries were built assuming static linking
|
||||||
|
set_property(TARGET witest.ubsan
|
||||||
|
PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded")
|
||||||
|
|
||||||
|
target_sources(witest.ubsan PUBLIC
|
||||||
|
${COMMON_SOURCES}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../WinVerifyTrustTest.cpp
|
||||||
|
)
|
|
@ -3,12 +3,15 @@
|
||||||
#include <wil/resource.h>
|
#include <wil/resource.h>
|
||||||
#include <wil/win32_helpers.h>
|
#include <wil/win32_helpers.h>
|
||||||
#include <wil/filesystem.h>
|
#include <wil/filesystem.h>
|
||||||
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||||
#include <wil/wrl.h>
|
#include <wil/wrl.h>
|
||||||
|
#endif
|
||||||
#include <wil/com.h>
|
#include <wil/com.h>
|
||||||
|
|
||||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <thread>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -96,7 +99,7 @@ void TestErrorCallbacks()
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
size_t const depthCount = 10;
|
constexpr size_t depthCount = 10;
|
||||||
for (size_t index = 0; index < depthCount; index++)
|
for (size_t index = 0; index < depthCount; index++)
|
||||||
{
|
{
|
||||||
LOG_HR(E_ACCESSDENIED);
|
LOG_HR(E_ACCESSDENIED);
|
||||||
|
@ -146,7 +149,7 @@ void StressErrorCallbacks()
|
||||||
{
|
{
|
||||||
auto restore = witest::AssignTemporaryValue(&wil::g_fResultOutputDebugString, false);
|
auto restore = witest::AssignTemporaryValue(&wil::g_fResultOutputDebugString, false);
|
||||||
|
|
||||||
size_t const threadCount = 20;
|
constexpr size_t threadCount = 20;
|
||||||
wil::unique_event eventArray[threadCount];
|
wil::unique_event eventArray[threadCount];
|
||||||
|
|
||||||
for (size_t index = 0; index < threadCount; index++)
|
for (size_t index = 0; index < threadCount; index++)
|
||||||
|
@ -164,7 +167,7 @@ void StressErrorCallbacks()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("WindowsInternalTests::ResultMacrosStress", "[!hide][result_macros][stress]")
|
TEST_CASE("WindowsInternalTests::ResultMacrosStress", "[LocalOnly][result_macros][stress]")
|
||||||
{
|
{
|
||||||
auto restore = witest::AssignTemporaryValue(&wil::g_pfnResultLoggingCallback, EmptyResultMacrosLoggingCallback);
|
auto restore = witest::AssignTemporaryValue(&wil::g_pfnResultLoggingCallback, EmptyResultMacrosLoggingCallback);
|
||||||
StressErrorCallbacks();
|
StressErrorCallbacks();
|
||||||
|
@ -485,8 +488,8 @@ TEST_CASE("WindowsInternalTests::ResultMacros", "[result_macros]")
|
||||||
REQUIRE_RETURNS_EXPECTED(E_hrNtAssertionFailure, [] { RETURN_IF_NTSTATUS_FAILED_EXPECTED(STATUS_ASSERTION_FAILURE); return S_OK; });
|
REQUIRE_RETURNS_EXPECTED(E_hrNtAssertionFailure, [] { RETURN_IF_NTSTATUS_FAILED_EXPECTED(STATUS_ASSERTION_FAILURE); return S_OK; });
|
||||||
REQUIRE_THROWS_RESULT(E_hrNtAssertionFailure, [] { THROW_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE); });
|
REQUIRE_THROWS_RESULT(E_hrNtAssertionFailure, [] { THROW_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE); });
|
||||||
REQUIRE_THROWS_MSG(E_hrNtAssertionFailure, [] { THROW_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); });
|
REQUIRE_THROWS_MSG(E_hrNtAssertionFailure, [] { THROW_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); });
|
||||||
REQUIRE_LOG(E_hrNtAssertionFailure, [] { REQUIRE(STATUS_ASSERTION_FAILURE == LOG_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE)); });
|
REQUIRE_LOG(E_hrNtAssertionFailure, [] { REQUIRE(STATUS_ASSERTION_FAILURE == static_cast<DWORD>(LOG_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE))); });
|
||||||
REQUIRE_LOG_MSG(E_hrNtAssertionFailure, [] { REQUIRE(STATUS_ASSERTION_FAILURE == LOG_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__)); });
|
REQUIRE_LOG_MSG(E_hrNtAssertionFailure, [] { REQUIRE(STATUS_ASSERTION_FAILURE == static_cast<DWORD>(LOG_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__))); });
|
||||||
REQUIRE_FAILFAST(E_hrNtAssertionFailure, [] { FAIL_FAST_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE); });
|
REQUIRE_FAILFAST(E_hrNtAssertionFailure, [] { FAIL_FAST_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE); });
|
||||||
REQUIRE_FAILFAST_MSG(E_hrNtAssertionFailure, [] { FAIL_FAST_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); });
|
REQUIRE_FAILFAST_MSG(E_hrNtAssertionFailure, [] { FAIL_FAST_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); });
|
||||||
|
|
||||||
|
@ -495,8 +498,8 @@ TEST_CASE("WindowsInternalTests::ResultMacros", "[result_macros]")
|
||||||
REQUIRE_RETURNS_EXPECTED(E_OUTOFMEMORY, [] { RETURN_IF_NTSTATUS_FAILED_EXPECTED(STATUS_NO_MEMORY); return S_OK; });
|
REQUIRE_RETURNS_EXPECTED(E_OUTOFMEMORY, [] { RETURN_IF_NTSTATUS_FAILED_EXPECTED(STATUS_NO_MEMORY); return S_OK; });
|
||||||
REQUIRE_THROWS_RESULT(E_OUTOFMEMORY, [] { THROW_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY); });
|
REQUIRE_THROWS_RESULT(E_OUTOFMEMORY, [] { THROW_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY); });
|
||||||
REQUIRE_THROWS_MSG(E_OUTOFMEMORY, [] { THROW_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__); });
|
REQUIRE_THROWS_MSG(E_OUTOFMEMORY, [] { THROW_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__); });
|
||||||
REQUIRE_LOG(E_OUTOFMEMORY, [] { REQUIRE(STATUS_NO_MEMORY == LOG_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY)); });
|
REQUIRE_LOG(E_OUTOFMEMORY, [] { REQUIRE(STATUS_NO_MEMORY == static_cast<DWORD>(LOG_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY))); });
|
||||||
REQUIRE_LOG_MSG(E_OUTOFMEMORY, [] { REQUIRE(STATUS_NO_MEMORY == LOG_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__)); });
|
REQUIRE_LOG_MSG(E_OUTOFMEMORY, [] { REQUIRE(STATUS_NO_MEMORY == static_cast<DWORD>(LOG_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__))); });
|
||||||
REQUIRE_FAILFAST(E_OUTOFMEMORY, [] { FAIL_FAST_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY); });
|
REQUIRE_FAILFAST(E_OUTOFMEMORY, [] { FAIL_FAST_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY); });
|
||||||
REQUIRE_FAILFAST_MSG(E_OUTOFMEMORY, [] { FAIL_FAST_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__); });
|
REQUIRE_FAILFAST_MSG(E_OUTOFMEMORY, [] { FAIL_FAST_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__); });
|
||||||
|
|
||||||
|
@ -1015,9 +1018,13 @@ TEST_CASE("WindowsInternalTests::UniqueHandle", "[resource][unique_any]")
|
||||||
wchar_t tempFileName[MAX_PATH];
|
wchar_t tempFileName[MAX_PATH];
|
||||||
REQUIRE_SUCCEEDED(witest::GetTempFileName(tempFileName));
|
REQUIRE_SUCCEEDED(witest::GetTempFileName(tempFileName));
|
||||||
|
|
||||||
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||||
CREATEFILE2_EXTENDED_PARAMETERS params = { sizeof(params) };
|
CREATEFILE2_EXTENDED_PARAMETERS params = { sizeof(params) };
|
||||||
params.dwFileAttributes = FILE_ATTRIBUTE_TEMPORARY;
|
params.dwFileAttributes = FILE_ATTRIBUTE_TEMPORARY;
|
||||||
wil::unique_hfile spValidHandle(::CreateFile2(tempFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, CREATE_ALWAYS, ¶ms));
|
wil::unique_hfile spValidHandle(::CreateFile2(tempFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, CREATE_ALWAYS, ¶ms));
|
||||||
|
#else
|
||||||
|
wil::unique_hfile spValidHandle(::CreateFileW(tempFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, nullptr));
|
||||||
|
#endif
|
||||||
|
|
||||||
::DeleteFileW(tempFileName);
|
::DeleteFileW(tempFileName);
|
||||||
REQUIRE(spValidHandle.get() != INVALID_HANDLE_VALUE);
|
REQUIRE(spValidHandle.get() != INVALID_HANDLE_VALUE);
|
||||||
|
@ -1072,9 +1079,13 @@ TEST_CASE("WindowsInternalTests::UniqueHandle", "[resource][unique_any]")
|
||||||
wchar_t tempFileName2[MAX_PATH];
|
wchar_t tempFileName2[MAX_PATH];
|
||||||
REQUIRE_SUCCEEDED(witest::GetTempFileName(tempFileName2));
|
REQUIRE_SUCCEEDED(witest::GetTempFileName(tempFileName2));
|
||||||
|
|
||||||
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||||
CREATEFILE2_EXTENDED_PARAMETERS params2 = { sizeof(params2) };
|
CREATEFILE2_EXTENDED_PARAMETERS params2 = { sizeof(params2) };
|
||||||
params2.dwFileAttributes = FILE_ATTRIBUTE_TEMPORARY;
|
params2.dwFileAttributes = FILE_ATTRIBUTE_TEMPORARY;
|
||||||
*(&spMoveHandle) = ::CreateFile2(tempFileName2, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, CREATE_ALWAYS, ¶ms2);
|
*(&spMoveHandle) = ::CreateFile2(tempFileName2, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, CREATE_ALWAYS, ¶ms2);
|
||||||
|
#else
|
||||||
|
*(&spMoveHandle) = ::CreateFileW(tempFileName2, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, nullptr);
|
||||||
|
#endif
|
||||||
|
|
||||||
::DeleteFileW(tempFileName2);
|
::DeleteFileW(tempFileName2);
|
||||||
REQUIRE(spMoveHandle);
|
REQUIRE(spMoveHandle);
|
||||||
|
@ -1434,6 +1445,62 @@ void SemaphoreTestCommon()
|
||||||
REQUIRE(eManual.try_open(L"BAR-TEST"));
|
REQUIRE(eManual.try_open(L"BAR-TEST"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename test_t>
|
||||||
|
void MutexRaiiTests()
|
||||||
|
{
|
||||||
|
test_t var1;
|
||||||
|
var1.create();
|
||||||
|
|
||||||
|
{
|
||||||
|
REQUIRE(var1.acquire());
|
||||||
|
}
|
||||||
|
|
||||||
|
// try_create
|
||||||
|
bool exists = false;
|
||||||
|
REQUIRE(var1.try_create(L"wiltestmutex", 0, MUTEX_ALL_ACCESS, nullptr, &exists));
|
||||||
|
REQUIRE_FALSE(exists);
|
||||||
|
test_t var2;
|
||||||
|
REQUIRE(var2.try_create(L"wiltestmutex", 0, MUTEX_ALL_ACCESS, nullptr, &exists));
|
||||||
|
REQUIRE(exists);
|
||||||
|
test_t var3;
|
||||||
|
REQUIRE_FALSE(var3.try_create(L"\\illegal\\chars\\too\\\\many\\\\namespaces", 0, MUTEX_ALL_ACCESS, nullptr, &exists));
|
||||||
|
REQUIRE(::GetLastError() != ERROR_SUCCESS);
|
||||||
|
|
||||||
|
// try_open
|
||||||
|
test_t var4;
|
||||||
|
REQUIRE_FALSE(var4.try_open(L"\\illegal\\chars\\too\\\\many\\\\namespaces"));
|
||||||
|
REQUIRE(::GetLastError() != ERROR_SUCCESS);
|
||||||
|
REQUIRE(var4.try_open(L"wiltestmutex"));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename test_t>
|
||||||
|
void SemaphoreRaiiTests()
|
||||||
|
{
|
||||||
|
test_t var1;
|
||||||
|
var1.create(1, 1);
|
||||||
|
|
||||||
|
{
|
||||||
|
REQUIRE(var1.acquire());
|
||||||
|
}
|
||||||
|
|
||||||
|
// try_create
|
||||||
|
bool exists = false;
|
||||||
|
REQUIRE(var1.try_create(1, 1, L"wiltestsemaphore", MUTEX_ALL_ACCESS, nullptr, &exists));
|
||||||
|
REQUIRE_FALSE(exists);
|
||||||
|
test_t var2;
|
||||||
|
REQUIRE(var2.try_create(1, 1, L"wiltestsemaphore", MUTEX_ALL_ACCESS, nullptr, &exists));
|
||||||
|
REQUIRE(exists);
|
||||||
|
test_t var3;
|
||||||
|
REQUIRE_FALSE(var3.try_create(1, 1, L"\\illegal\\chars\\too\\\\many\\\\namespaces", MUTEX_ALL_ACCESS, nullptr, &exists));
|
||||||
|
REQUIRE(::GetLastError() != ERROR_SUCCESS);
|
||||||
|
|
||||||
|
// try_open
|
||||||
|
test_t var4;
|
||||||
|
REQUIRE_FALSE(var4.try_open(L"\\illegal\\chars\\too\\\\many\\\\namespaces"));
|
||||||
|
REQUIRE(::GetLastError() != ERROR_SUCCESS);
|
||||||
|
REQUIRE(var4.try_open(L"wiltestsemaphore"));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("WindowsInternalTests::HandleWrappers", "[resource][unique_any]")
|
TEST_CASE("WindowsInternalTests::HandleWrappers", "[resource][unique_any]")
|
||||||
{
|
{
|
||||||
EventTestCommon<wil::unique_event_nothrow>();
|
EventTestCommon<wil::unique_event_nothrow>();
|
||||||
|
@ -1467,15 +1534,17 @@ TEST_CASE("WindowsInternalTests::HandleWrappers", "[resource][unique_any]")
|
||||||
wil::unique_event_nothrow testEventNoExcept;
|
wil::unique_event_nothrow testEventNoExcept;
|
||||||
REQUIRE(SUCCEEDED(testEventNoExcept.create(wil::EventOptions::ManualReset)));
|
REQUIRE(SUCCEEDED(testEventNoExcept.create(wil::EventOptions::ManualReset)));
|
||||||
|
|
||||||
|
|
||||||
MutexTestCommon<wil::unique_mutex_nothrow>();
|
MutexTestCommon<wil::unique_mutex_nothrow>();
|
||||||
MutexTestCommon<wil::unique_mutex_failfast>();
|
MutexTestCommon<wil::unique_mutex_failfast>();
|
||||||
|
MutexRaiiTests<wil::unique_mutex_nothrow>();
|
||||||
|
MutexRaiiTests<wil::unique_mutex_failfast>();
|
||||||
|
|
||||||
// intentionally disabled in the non-exception version...
|
// intentionally disabled in the non-exception version...
|
||||||
// wil::unique_mutex_nothrow testMutex2(L"FOO-TEST-2");
|
// wil::unique_mutex_nothrow testMutex2(L"FOO-TEST-2");
|
||||||
wil::unique_mutex_failfast testMutex3(L"FOO-TEST-3");
|
wil::unique_mutex_failfast testMutex3(L"FOO-TEST-3");
|
||||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
MutexTestCommon<wil::unique_mutex>();
|
MutexTestCommon<wil::unique_mutex>();
|
||||||
|
MutexRaiiTests<wil::unique_mutex>();
|
||||||
|
|
||||||
wil::unique_mutex testMutex(L"FOO-TEST");
|
wil::unique_mutex testMutex(L"FOO-TEST");
|
||||||
WaitForSingleObjectEx(testMutex.get(), INFINITE, TRUE);
|
WaitForSingleObjectEx(testMutex.get(), INFINITE, TRUE);
|
||||||
|
@ -1494,12 +1563,15 @@ TEST_CASE("WindowsInternalTests::HandleWrappers", "[resource][unique_any]")
|
||||||
|
|
||||||
SemaphoreTestCommon<wil::unique_semaphore_nothrow>();
|
SemaphoreTestCommon<wil::unique_semaphore_nothrow>();
|
||||||
SemaphoreTestCommon<wil::unique_semaphore_failfast>();
|
SemaphoreTestCommon<wil::unique_semaphore_failfast>();
|
||||||
|
SemaphoreRaiiTests<wil::unique_semaphore_nothrow>();
|
||||||
|
SemaphoreRaiiTests<wil::unique_semaphore_failfast>();
|
||||||
|
|
||||||
// intentionally disabled in the non-exception version...
|
// intentionally disabled in the non-exception version...
|
||||||
// wil::unique_semaphore_nothrow testSemaphore2(1, 1);
|
// wil::unique_semaphore_nothrow testSemaphore2(1, 1);
|
||||||
wil::unique_semaphore_failfast testSemaphore3(1, 1);
|
wil::unique_semaphore_failfast testSemaphore3(1, 1);
|
||||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
SemaphoreTestCommon<wil::unique_semaphore>();
|
SemaphoreTestCommon<wil::unique_semaphore>();
|
||||||
|
SemaphoreRaiiTests<wil::unique_semaphore>();
|
||||||
|
|
||||||
wil::unique_semaphore testSemaphore(1, 1);
|
wil::unique_semaphore testSemaphore(1, 1);
|
||||||
WaitForSingleObjectEx(testSemaphore.get(), INFINITE, true);
|
WaitForSingleObjectEx(testSemaphore.get(), INFINITE, true);
|
||||||
|
@ -1683,6 +1755,15 @@ TEST_CASE("WindowsInternalTests::HandleWrappers", "[resource][unique_any]")
|
||||||
auto unique_bstr_nothrow2 = wil::make_bstr_nothrow(L"");
|
auto unique_bstr_nothrow2 = wil::make_bstr_nothrow(L"");
|
||||||
REQUIRE(wcscmp(L"", unique_bstr_nothrow2.get()) == 0);
|
REQUIRE(wcscmp(L"", unique_bstr_nothrow2.get()) == 0);
|
||||||
|
|
||||||
|
auto unique_variant_bstr_failfast1 = wil::make_variant_bstr_failfast(L"Foo");
|
||||||
|
REQUIRE(wcscmp(L"Foo", V_UNION(unique_variant_bstr_failfast1.addressof(), bstrVal)) == 0);
|
||||||
|
|
||||||
|
auto unique_variant_bstr_nothrow1 = wil::make_variant_bstr_nothrow(L"Foo");
|
||||||
|
REQUIRE(wcscmp(L"Foo", V_UNION(unique_variant_bstr_nothrow1.addressof(), bstrVal)) == 0);
|
||||||
|
|
||||||
|
auto unique_variant_bstr_nothrow2 = wil::make_variant_bstr_nothrow(L"");
|
||||||
|
REQUIRE(wcscmp(L"", V_UNION(unique_variant_bstr_nothrow2.addressof(), bstrVal)) == 0);
|
||||||
|
|
||||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||||
auto unique_bstr_te1 = wil::make_bstr(L"Foo");
|
auto unique_bstr_te1 = wil::make_bstr(L"Foo");
|
||||||
REQUIRE(wcscmp(L"Foo", unique_bstr_te1.get()) == 0);
|
REQUIRE(wcscmp(L"Foo", unique_bstr_te1.get()) == 0);
|
||||||
|
@ -1690,6 +1771,11 @@ TEST_CASE("WindowsInternalTests::HandleWrappers", "[resource][unique_any]")
|
||||||
auto unique_bstr_te2 = wil::make_bstr(L"");
|
auto unique_bstr_te2 = wil::make_bstr(L"");
|
||||||
REQUIRE(wcscmp(L"", unique_bstr_te2.get()) == 0);
|
REQUIRE(wcscmp(L"", unique_bstr_te2.get()) == 0);
|
||||||
|
|
||||||
|
auto unique_variant_bstr_te1 = wil::make_variant_bstr(L"Foo");
|
||||||
|
REQUIRE(wcscmp(L"Foo", V_UNION(unique_variant_bstr_te1.addressof(), bstrVal)) == 0);
|
||||||
|
|
||||||
|
auto unique_variant_bstr_te2 = wil::make_variant_bstr(L"");
|
||||||
|
REQUIRE(wcscmp(L"", V_UNION(unique_variant_bstr_te2.addressof(), bstrVal)) == 0);
|
||||||
|
|
||||||
auto testString = wil::make_cotaskmem_string(L"Foo");
|
auto testString = wil::make_cotaskmem_string(L"Foo");
|
||||||
{
|
{
|
||||||
|
@ -1774,11 +1860,14 @@ TEST_CASE("WindowsInternalTests::Locking", "[resource]")
|
||||||
{
|
{
|
||||||
CRITICAL_SECTION cs;
|
CRITICAL_SECTION cs;
|
||||||
::InitializeCriticalSectionEx(&cs, 0, 0);
|
::InitializeCriticalSectionEx(&cs, 0, 0);
|
||||||
|
{
|
||||||
auto lock = wil::EnterCriticalSection(&cs);
|
auto lock = wil::EnterCriticalSection(&cs);
|
||||||
REQUIRE(lock);
|
REQUIRE(lock);
|
||||||
auto tryLock = wil::TryEnterCriticalSection(&cs);
|
auto tryLock = wil::TryEnterCriticalSection(&cs);
|
||||||
REQUIRE(tryLock);
|
REQUIRE(tryLock);
|
||||||
}
|
}
|
||||||
|
::DeleteCriticalSection(&cs);
|
||||||
|
}
|
||||||
{
|
{
|
||||||
wil::critical_section cs;
|
wil::critical_section cs;
|
||||||
auto lock = cs.lock();
|
auto lock = cs.lock();
|
||||||
|
@ -2451,6 +2540,43 @@ TEST_CASE("WindowsInternalTests::Win32HelperTests", "[win32_helpers]")
|
||||||
REQUIRE(systemTimePlusOneHour64 == (systemTime64 + wil::filetime_duration::one_hour));
|
REQUIRE(systemTimePlusOneHour64 == (systemTime64 + wil::filetime_duration::one_hour));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("WindowsInternalTests::RectHelperTests", "[win32_helpers]")
|
||||||
|
{
|
||||||
|
RECT rect{ 50, 100, 200, 300 };
|
||||||
|
POINT leftEdgePoint{ 50, 150 };
|
||||||
|
POINT topEdgePoint{ 100, 100 };
|
||||||
|
POINT rightEdgePoint{ 200, 150 };
|
||||||
|
POINT bottomEdgePoint{ 100, 300 };
|
||||||
|
POINT insidePoint{ 150, 150};
|
||||||
|
|
||||||
|
RECT emptyRectAtOrigin{};
|
||||||
|
RECT emptyRectNotAtOrigin{ 50, 50, 50, 50 };
|
||||||
|
RECT nonNormalizedRect{ 300, 300, 0, 0 };
|
||||||
|
|
||||||
|
REQUIRE(wil::rect_width(rect) == 150);
|
||||||
|
REQUIRE(wil::rect_height(rect) == 200);
|
||||||
|
|
||||||
|
// rect_is_empty should work like user32's IsRectEmpty
|
||||||
|
REQUIRE_FALSE(wil::rect_is_empty(rect));
|
||||||
|
REQUIRE(wil::rect_is_empty(emptyRectAtOrigin));
|
||||||
|
REQUIRE(wil::rect_is_empty(emptyRectNotAtOrigin));
|
||||||
|
REQUIRE(wil::rect_is_empty(nonNormalizedRect));
|
||||||
|
|
||||||
|
// rect_contains_point should work like user32's PtInRect
|
||||||
|
REQUIRE(wil::rect_contains_point(rect, insidePoint));
|
||||||
|
REQUIRE(wil::rect_contains_point(rect, leftEdgePoint));
|
||||||
|
REQUIRE(wil::rect_contains_point(rect, topEdgePoint));
|
||||||
|
REQUIRE_FALSE(wil::rect_contains_point(rect, rightEdgePoint));
|
||||||
|
REQUIRE_FALSE(wil::rect_contains_point(rect, bottomEdgePoint));
|
||||||
|
REQUIRE_FALSE(wil::rect_contains_point(nonNormalizedRect, insidePoint));
|
||||||
|
|
||||||
|
auto rectFromSize = wil::rect_from_size<RECT>(50, 100, 150, 200);
|
||||||
|
REQUIRE(rectFromSize.left == rect.left);
|
||||||
|
REQUIRE(rectFromSize.top == rect.top);
|
||||||
|
REQUIRE(rectFromSize.right == rect.right);
|
||||||
|
REQUIRE(rectFromSize.bottom == rect.bottom);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("WindowsInternalTests::InitOnceNonTests")
|
TEST_CASE("WindowsInternalTests::InitOnceNonTests")
|
||||||
{
|
{
|
||||||
bool called = false;
|
bool called = false;
|
||||||
|
@ -2494,7 +2620,8 @@ TEST_CASE("WindowsInternalTests::InitOnceNonTests")
|
||||||
init = {};
|
init = {};
|
||||||
|
|
||||||
// A thrown exception leaves the object un-initialized
|
// A thrown exception leaves the object un-initialized
|
||||||
REQUIRE_THROWS_AS(winner = wil::init_once(init, [&] { called = true; throw wil::ResultException(E_FAIL); }), wil::ResultException);
|
static volatile bool always_true = true; // So that the compiler can't determine that we unconditionally throw below (warning C4702)
|
||||||
|
REQUIRE_THROWS_AS(winner = wil::init_once(init, [&] { called = true; THROW_HR_IF(E_FAIL, always_true); }), wil::ResultException);
|
||||||
REQUIRE_FALSE(wil::init_once_initialized(init));
|
REQUIRE_FALSE(wil::init_once_initialized(init));
|
||||||
REQUIRE(called);
|
REQUIRE(called);
|
||||||
REQUIRE_FALSE(winner);
|
REQUIRE_FALSE(winner);
|
||||||
|
@ -2930,7 +3057,7 @@ TEST_CASE("WindowsInternalTests::TestUniqueArrayCases", "[resource]")
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef __cplusplus_winrt
|
#if !defined(__cplusplus_winrt) && (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||||
TEST_CASE("WindowsInternalTests::VerifyMakeAgileCallback", "[wrl]")
|
TEST_CASE("WindowsInternalTests::VerifyMakeAgileCallback", "[wrl]")
|
||||||
{
|
{
|
||||||
using namespace ABI::Windows::Foundation;
|
using namespace ABI::Windows::Foundation;
|
||||||
|
@ -2987,6 +3114,7 @@ TEST_CASE("WindowsInternalTests::Ranges", "[common]")
|
||||||
{
|
{
|
||||||
++count;
|
++count;
|
||||||
m = 1;
|
m = 1;
|
||||||
|
(void)m;
|
||||||
}
|
}
|
||||||
REQUIRE(ARRAYSIZE(things) == count);
|
REQUIRE(ARRAYSIZE(things) == count);
|
||||||
REQUIRE(0 == things[0]);
|
REQUIRE(0 == things[0]);
|
||||||
|
@ -3084,12 +3212,12 @@ void ThreadPoolWaitTestHelper(bool requireExactCallbackCount)
|
||||||
REQUIRE_SUCCEEDED(myContext.Event.create());
|
REQUIRE_SUCCEEDED(myContext.Event.create());
|
||||||
|
|
||||||
WaitResourceT wait;
|
WaitResourceT wait;
|
||||||
wait.reset(CreateThreadpoolWait(ThreadPoolWaitTestCallback, &myContext, NULL));
|
wait.reset(CreateThreadpoolWait(ThreadPoolWaitTestCallback, &myContext, nullptr));
|
||||||
REQUIRE(wait);
|
REQUIRE(wait);
|
||||||
|
|
||||||
SetThreadpoolWait(wait.get(), myContext.Event.get(), nullptr);
|
SetThreadpoolWait(wait.get(), myContext.Event.get(), nullptr);
|
||||||
|
|
||||||
const int loopCount = 5;
|
constexpr int loopCount = 5;
|
||||||
for (int currCallbackCount = 0; currCallbackCount != loopCount; ++currCallbackCount)
|
for (int currCallbackCount = 0; currCallbackCount != loopCount; ++currCallbackCount)
|
||||||
{
|
{
|
||||||
// Signal event.
|
// Signal event.
|
||||||
|
@ -3149,10 +3277,10 @@ void ThreadPoolWaitWorkHelper(bool requireExactCallbackCount)
|
||||||
ThreadPoolWaitWorkContext myContext;
|
ThreadPoolWaitWorkContext myContext;
|
||||||
|
|
||||||
WaitResourceT work;
|
WaitResourceT work;
|
||||||
work.reset(CreateThreadpoolWork(ThreadPoolWaitWorkCallback, &myContext, NULL));
|
work.reset(CreateThreadpoolWork(ThreadPoolWaitWorkCallback, &myContext, nullptr));
|
||||||
REQUIRE(work);
|
REQUIRE(work);
|
||||||
|
|
||||||
const int loopCount = 5;
|
constexpr int loopCount = 5;
|
||||||
for (int itr = 0; itr != loopCount; ++itr)
|
for (int itr = 0; itr != loopCount; ++itr)
|
||||||
{
|
{
|
||||||
SubmitThreadpoolWork(work.get());
|
SubmitThreadpoolWork(work.get());
|
||||||
|
@ -3202,7 +3330,7 @@ void ThreadPoolTimerWorkHelper(SetThreadpoolTimerT const &setThreadpoolTimerFn,
|
||||||
timer.reset(CreateThreadpoolTimer(ThreadPoolTimerWorkCallback, &myContext, nullptr));
|
timer.reset(CreateThreadpoolTimer(ThreadPoolTimerWorkCallback, &myContext, nullptr));
|
||||||
REQUIRE(timer);
|
REQUIRE(timer);
|
||||||
|
|
||||||
const int loopCount = 5;
|
constexpr int loopCount = 5;
|
||||||
for (int currCallbackCount = 0; currCallbackCount != loopCount; ++currCallbackCount)
|
for (int currCallbackCount = 0; currCallbackCount != loopCount; ++currCallbackCount)
|
||||||
{
|
{
|
||||||
// Schedule timer
|
// Schedule timer
|
||||||
|
@ -3256,7 +3384,7 @@ TEST_CASE("WindowsInternalTests::ThreadPoolTimerTest", "[resource][unique_thread
|
||||||
ThreadPoolTimerWorkHelper<wil::unique_threadpool_timer_nocancel, FILETIME>(SetThreadpoolTimer, true);
|
ThreadPoolTimerWorkHelper<wil::unique_threadpool_timer_nocancel, FILETIME>(SetThreadpoolTimer, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||||
static void __stdcall SlimEventTrollCallback(
|
static void __stdcall SlimEventTrollCallback(
|
||||||
_Inout_ PTP_CALLBACK_INSTANCE /*instance*/,
|
_Inout_ PTP_CALLBACK_INSTANCE /*instance*/,
|
||||||
_Inout_opt_ void* context,
|
_Inout_opt_ void* context,
|
||||||
|
@ -3328,7 +3456,7 @@ TEST_CASE("WindowsInternalTests::SlimEventTests", "[resource][slim_event]")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||||
|
|
||||||
struct ConditionVariableCSCallbackContext
|
struct ConditionVariableCSCallbackContext
|
||||||
{
|
{
|
||||||
|
@ -3469,4 +3597,88 @@ TEST_CASE("WindowsInternalTests::ShutdownAwareObjectAlignmentTests", "[result_ma
|
||||||
VerifyAlignment<wil::object_without_destructor_on_shutdown>();
|
VerifyAlignment<wil::object_without_destructor_on_shutdown>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
|
||||||
|
TEST_CASE("WindowsInternalTests::ModuleReference", "[wrl]")
|
||||||
|
{
|
||||||
|
REQUIRE(::Microsoft::WRL::GetModuleBase() == nullptr);
|
||||||
|
// Executables don't have a ModuleBase, so we need to create one.
|
||||||
|
struct FakeModuleBase : Microsoft::WRL::Details::ModuleBase
|
||||||
|
{
|
||||||
|
unsigned long count = 42;
|
||||||
|
STDMETHOD_(unsigned long, IncrementObjectCount)()
|
||||||
|
{
|
||||||
|
return InterlockedIncrement(&count);
|
||||||
|
}
|
||||||
|
STDMETHOD_(unsigned long, DecrementObjectCount)()
|
||||||
|
{
|
||||||
|
return InterlockedDecrement(&count);
|
||||||
|
}
|
||||||
|
STDMETHOD_(unsigned long, GetObjectCount)() const
|
||||||
|
{
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
// Dummy implementations of everything else (never called).
|
||||||
|
STDMETHOD_(const Microsoft::WRL::Details::CreatorMap**, GetFirstEntryPointer)() const { return nullptr; }
|
||||||
|
STDMETHOD_(const Microsoft::WRL::Details::CreatorMap**, GetMidEntryPointer)() const { return nullptr; }
|
||||||
|
STDMETHOD_(const Microsoft::WRL::Details::CreatorMap**, GetLastEntryPointer)() const { return nullptr; }
|
||||||
|
STDMETHOD_(SRWLOCK*, GetLock)() const { return nullptr; }
|
||||||
|
STDMETHOD(RegisterWinRTObject)(const wchar_t*, const wchar_t**, _Inout_ RO_REGISTRATION_COOKIE*, unsigned int) { return E_NOTIMPL; }
|
||||||
|
STDMETHOD(UnregisterWinRTObject)(const wchar_t*, _In_ RO_REGISTRATION_COOKIE) { return E_NOTIMPL; }
|
||||||
|
STDMETHOD(RegisterCOMObject)(const wchar_t*, _In_ IID*, _In_ IClassFactory**, _Inout_ DWORD*, unsigned int) { return E_NOTIMPL; }
|
||||||
|
STDMETHOD(UnregisterCOMObject)(const wchar_t*, _Inout_ DWORD*, unsigned int) { return E_NOTIMPL; }
|
||||||
|
|
||||||
|
};
|
||||||
|
FakeModuleBase fake;
|
||||||
|
|
||||||
|
auto peek_module_ref_count = []()
|
||||||
|
{
|
||||||
|
return ::Microsoft::WRL::GetModuleBase()->GetObjectCount();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto initial = peek_module_ref_count();
|
||||||
|
|
||||||
|
// Basic test: Construct and destruct.
|
||||||
|
{
|
||||||
|
auto module_ref = wil::wrl_module_reference();
|
||||||
|
REQUIRE(peek_module_ref_count() == initial + 1);
|
||||||
|
}
|
||||||
|
REQUIRE(peek_module_ref_count() == initial);
|
||||||
|
|
||||||
|
// Fancy test: Copy object with embedded reference.
|
||||||
|
{
|
||||||
|
struct object_with_ref
|
||||||
|
{
|
||||||
|
wil::wrl_module_reference ref;
|
||||||
|
};
|
||||||
|
object_with_ref o1;
|
||||||
|
REQUIRE(peek_module_ref_count() == initial + 1);
|
||||||
|
auto o2 = o1;
|
||||||
|
REQUIRE(peek_module_ref_count() == initial + 2);
|
||||||
|
o1 = o2;
|
||||||
|
REQUIRE(peek_module_ref_count() == initial + 2);
|
||||||
|
o2 = std::move(o1);
|
||||||
|
REQUIRE(peek_module_ref_count() == initial + 2);
|
||||||
|
}
|
||||||
|
REQUIRE(peek_module_ref_count() == initial);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(WIL_ENABLE_EXCEPTIONS) && (defined(NTDDI_WIN10_CO) ? \
|
||||||
|
WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES) : \
|
||||||
|
WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES))
|
||||||
|
TEST_CASE("WindowsInternalTests::VerifyModuleReferencesForThread", "[win32_helpers]")
|
||||||
|
{
|
||||||
|
bool success = true;
|
||||||
|
std::thread([&]
|
||||||
|
{
|
||||||
|
auto moduleRef = wil::get_module_reference_for_thread();
|
||||||
|
moduleRef.reset(); // results in exiting the thread
|
||||||
|
// should never get here
|
||||||
|
success = false;
|
||||||
|
FAIL();
|
||||||
|
}).join();
|
||||||
|
REQUIRE(success);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
add_executable(witest.win7)
|
||||||
|
|
||||||
|
target_compile_definitions(witest.win7 PRIVATE
|
||||||
|
-D_WIN32_WINNT=0x0601
|
||||||
|
)
|
||||||
|
|
||||||
|
target_sources(witest.win7 PRIVATE
|
||||||
|
${COMMON_SOURCES}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp
|
||||||
|
)
|
|
@ -12,14 +12,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#endif // _MSC_VER
|
#endif // _MSC_VER
|
||||||
|
|
||||||
#ifdef __clang__
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wpragma-pack"
|
|
||||||
#pragma clang diagnostic ignored "-Wunused-value"
|
|
||||||
#pragma clang diagnostic ignored "-Wmicrosoft-sealed"
|
|
||||||
#pragma clang diagnostic ignored "-Winaccessible-base"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#pragma region includes
|
#pragma region includes
|
||||||
|
|
||||||
#include <inspectable.h>
|
#include <inspectable.h>
|
||||||
|
@ -599,6 +591,8 @@ struct VerifyInheritanceHelper<I, Nil>
|
||||||
|
|
||||||
} // namespace Details
|
} // namespace Details
|
||||||
|
|
||||||
|
// note: Due to potential shutdown ordering issues, the results of GetModuleBase
|
||||||
|
// should always be checked for null on reference counting and cleanup operations.
|
||||||
inline Details::ModuleBase* GetModuleBase() throw()
|
inline Details::ModuleBase* GetModuleBase() throw()
|
||||||
{
|
{
|
||||||
return Details::ModuleBase::module_;
|
return Details::ModuleBase::module_;
|
||||||
|
@ -840,7 +834,7 @@ struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck,
|
||||||
I0,
|
I0,
|
||||||
AdjustImplements<RuntimeClassFlagsT, true, TInterfaces...>::Type
|
AdjustImplements<RuntimeClassFlagsT, true, TInterfaces...>::Type
|
||||||
{
|
{
|
||||||
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
|
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TOtherInterfaces> friend struct ImplementsHelper;
|
||||||
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
|
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -890,7 +884,7 @@ struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck,
|
||||||
Selector<I0, ImplementsHelper<RuntimeClassFlagsT, doStrictCheck, ImplementsMarker<I0>, TInterfaces...>>,
|
Selector<I0, ImplementsHelper<RuntimeClassFlagsT, doStrictCheck, ImplementsMarker<I0>, TInterfaces...>>,
|
||||||
Selector<typename AdjustImplements<RuntimeClassFlagsT, true, TInterfaces...>::Type, ImplementsHelper<RuntimeClassFlagsT, doStrictCheck, ImplementsMarker<I0>, TInterfaces...>>
|
Selector<typename AdjustImplements<RuntimeClassFlagsT, true, TInterfaces...>::Type, ImplementsHelper<RuntimeClassFlagsT, doStrictCheck, ImplementsMarker<I0>, TInterfaces...>>
|
||||||
{
|
{
|
||||||
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
|
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TOtherInterfaces> friend struct ImplementsHelper;
|
||||||
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
|
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -935,7 +929,7 @@ struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck,
|
||||||
AdjustImplements<RuntimeClassFlagsT, doStrictCheck, I0>::Type,
|
AdjustImplements<RuntimeClassFlagsT, doStrictCheck, I0>::Type,
|
||||||
AdjustImplements<RuntimeClassFlagsT, true, I1, TInterfaces...>::Type
|
AdjustImplements<RuntimeClassFlagsT, true, I1, TInterfaces...>::Type
|
||||||
{
|
{
|
||||||
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
|
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TOtherInterfaces> friend struct ImplementsHelper;
|
||||||
template <unsigned int RuntimeClassTypeT> friend class Details::RuntimeClassBaseT;
|
template <unsigned int RuntimeClassTypeT> friend class Details::RuntimeClassBaseT;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -976,7 +970,7 @@ template <typename RuntimeClassFlagsT, bool doStrictCheck, typename I0>
|
||||||
struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck, CloakedIid<I0>> :
|
struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck, CloakedIid<I0>> :
|
||||||
AdjustImplements<RuntimeClassFlagsT, doStrictCheck, I0>::Type
|
AdjustImplements<RuntimeClassFlagsT, doStrictCheck, I0>::Type
|
||||||
{
|
{
|
||||||
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
|
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
|
||||||
template <unsigned int RuntimeClassTypeT> friend class Details::RuntimeClassBaseT;
|
template <unsigned int RuntimeClassTypeT> friend class Details::RuntimeClassBaseT;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -1012,7 +1006,7 @@ protected:
|
||||||
template <typename RuntimeClassFlagsT, bool doStrictCheck>
|
template <typename RuntimeClassFlagsT, bool doStrictCheck>
|
||||||
struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck>
|
struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck>
|
||||||
{
|
{
|
||||||
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
|
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
|
||||||
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
|
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -1041,7 +1035,7 @@ struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck,
|
||||||
ChainInterfaces<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9>,
|
ChainInterfaces<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9>,
|
||||||
AdjustImplements<RuntimeClassFlagsT, true, TInterfaces...>::Type
|
AdjustImplements<RuntimeClassFlagsT, true, TInterfaces...>::Type
|
||||||
{
|
{
|
||||||
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
|
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TOtherInterfaces> friend struct ImplementsHelper;
|
||||||
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
|
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -1086,7 +1080,7 @@ struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck,
|
||||||
{
|
{
|
||||||
static_assert(hasImplements, "Cannot use MixIn to with a class not deriving from \"Implements\"");
|
static_assert(hasImplements, "Cannot use MixIn to with a class not deriving from \"Implements\"");
|
||||||
|
|
||||||
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
|
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TOtherInterfaces> friend struct ImplementsHelper;
|
||||||
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
|
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -1148,7 +1142,7 @@ template <typename RuntimeClassFlagsT, typename FactoryInterface, bool doStrictC
|
||||||
struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck, ComposableBase<FactoryInterface>, TInterfaces...> :
|
struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck, ComposableBase<FactoryInterface>, TInterfaces...> :
|
||||||
ImplementsHelper<RuntimeClassFlagsT, true, ComposableBase<FactoryInterface>>
|
ImplementsHelper<RuntimeClassFlagsT, true, ComposableBase<FactoryInterface>>
|
||||||
{
|
{
|
||||||
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
|
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TOtherInterfaces> friend struct ImplementsHelper;
|
||||||
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
|
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -1184,7 +1178,7 @@ protected:
|
||||||
template <typename RuntimeClassFlagsT, typename FactoryInterface, bool doStrictCheck>
|
template <typename RuntimeClassFlagsT, typename FactoryInterface, bool doStrictCheck>
|
||||||
struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck, ComposableBase<FactoryInterface>>
|
struct __declspec(novtable) ImplementsHelper<RuntimeClassFlagsT, doStrictCheck, ComposableBase<FactoryInterface>>
|
||||||
{
|
{
|
||||||
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
|
template <typename OtherRuntimeClassFlagsT, bool OtherDoStrictCheck, typename ...TInterfaces> friend struct ImplementsHelper;
|
||||||
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
|
template <unsigned int RuntimeClassTypeT> friend class RuntimeClassBaseT;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -1282,7 +1276,7 @@ public:
|
||||||
typedef I0 FirstInterface;
|
typedef I0 FirstInterface;
|
||||||
protected:
|
protected:
|
||||||
typedef typename Details::AdjustImplements<RuntimeClassFlags<WinRt>, true, I0, TInterfaces...>::Type BaseType;
|
typedef typename Details::AdjustImplements<RuntimeClassFlags<WinRt>, true, I0, TInterfaces...>::Type BaseType;
|
||||||
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct Details::ImplementsHelper;
|
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TOtherInterfaces> friend struct Details::ImplementsHelper;
|
||||||
template <unsigned int RuntimeClassTypeT> friend class Details::RuntimeClassBaseT;
|
template <unsigned int RuntimeClassTypeT> friend class Details::RuntimeClassBaseT;
|
||||||
|
|
||||||
HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv) throw()
|
HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv) throw()
|
||||||
|
@ -1317,7 +1311,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
typedef typename Details::AdjustImplements<RuntimeClassFlags<flags>, true, I0, TInterfaces...>::Type BaseType;
|
typedef typename Details::AdjustImplements<RuntimeClassFlags<flags>, true, I0, TInterfaces...>::Type BaseType;
|
||||||
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TInterfaces> friend struct Details::ImplementsHelper;
|
template <typename RuntimeClassFlagsT, bool doStrictCheck, typename ...TOtherInterfaces> friend struct Details::ImplementsHelper;
|
||||||
template <unsigned int RuntimeClassTypeT> friend class Details::RuntimeClassBaseT;
|
template <unsigned int RuntimeClassTypeT> friend class Details::RuntimeClassBaseT;
|
||||||
|
|
||||||
HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv) throw()
|
HRESULT CanCastTo(REFIID riid, _Outptr_ void **ppv) throw()
|
||||||
|
@ -1523,6 +1517,8 @@ private:
|
||||||
#define UnknownInterlockedCompareExchangePointer InterlockedCompareExchangePointer
|
#define UnknownInterlockedCompareExchangePointer InterlockedCompareExchangePointer
|
||||||
#define UnknownInterlockedCompareExchangePointerForIncrement InterlockedCompareExchangePointer
|
#define UnknownInterlockedCompareExchangePointerForIncrement InterlockedCompareExchangePointer
|
||||||
#define UnknownInterlockedCompareExchangePointerForRelease InterlockedCompareExchangePointer
|
#define UnknownInterlockedCompareExchangePointerForRelease InterlockedCompareExchangePointer
|
||||||
|
#define UnknownInterlockedCompareExchangeForIncrement InterlockedCompareExchange
|
||||||
|
#define UnknownInterlockedCompareExchangeForRelease InterlockedCompareExchange
|
||||||
|
|
||||||
#elif defined(_ARM_)
|
#elif defined(_ARM_)
|
||||||
|
|
||||||
|
@ -1532,6 +1528,8 @@ private:
|
||||||
#define UnknownInterlockedCompareExchangePointer InterlockedCompareExchangePointer
|
#define UnknownInterlockedCompareExchangePointer InterlockedCompareExchangePointer
|
||||||
#define UnknownInterlockedCompareExchangePointerForIncrement InterlockedCompareExchangePointerNoFence
|
#define UnknownInterlockedCompareExchangePointerForIncrement InterlockedCompareExchangePointerNoFence
|
||||||
#define UnknownInterlockedCompareExchangePointerForRelease InterlockedCompareExchangePointerRelease
|
#define UnknownInterlockedCompareExchangePointerForRelease InterlockedCompareExchangePointerRelease
|
||||||
|
#define UnknownInterlockedCompareExchangeForIncrement InterlockedCompareExchangeNoFence
|
||||||
|
#define UnknownInterlockedCompareExchangeForRelease InterlockedCompareExchangeRelease
|
||||||
|
|
||||||
#elif defined(_ARM64_)
|
#elif defined(_ARM64_)
|
||||||
|
|
||||||
|
@ -1541,6 +1539,8 @@ private:
|
||||||
#define UnknownInterlockedCompareExchangePointer InterlockedCompareExchangePointer
|
#define UnknownInterlockedCompareExchangePointer InterlockedCompareExchangePointer
|
||||||
#define UnknownInterlockedCompareExchangePointerForIncrement InterlockedCompareExchangePointerNoFence
|
#define UnknownInterlockedCompareExchangePointerForIncrement InterlockedCompareExchangePointerNoFence
|
||||||
#define UnknownInterlockedCompareExchangePointerForRelease InterlockedCompareExchangePointerRelease
|
#define UnknownInterlockedCompareExchangePointerForRelease InterlockedCompareExchangePointerRelease
|
||||||
|
#define UnknownInterlockedCompareExchangeForIncrement InterlockedCompareExchangeNoFence
|
||||||
|
#define UnknownInterlockedCompareExchangeForRelease InterlockedCompareExchangeRelease
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
|
@ -1562,6 +1562,37 @@ class __declspec(novtable) RuntimeClassImpl;
|
||||||
// PREFast cannot see through template instantiation for AsIID()
|
// PREFast cannot see through template instantiation for AsIID()
|
||||||
#pragma warning(disable: 6388)
|
#pragma warning(disable: 6388)
|
||||||
|
|
||||||
|
// Reference counting functions that check overflow. If overflow is detected, ref count value will stop at LONG_MAX, and the object being
|
||||||
|
// reference-counted will be leaked.
|
||||||
|
inline unsigned long SafeUnknownIncrementReference(long volatile &refcount) throw()
|
||||||
|
{
|
||||||
|
long oldValue = refcount;
|
||||||
|
while (oldValue != LONG_MAX && (UnknownInterlockedCompareExchangeForIncrement(&refcount, oldValue + 1, oldValue) != oldValue))
|
||||||
|
{
|
||||||
|
oldValue = refcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldValue != LONG_MAX)
|
||||||
|
{
|
||||||
|
return oldValue + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return LONG_MAX;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned long SafeUnknownDecrementReference(long volatile &refcount) throw()
|
||||||
|
{
|
||||||
|
long oldValue = refcount;
|
||||||
|
while (oldValue != LONG_MAX && (UnknownInterlockedCompareExchangeForRelease(&refcount, oldValue - 1, oldValue) != oldValue))
|
||||||
|
{
|
||||||
|
oldValue = refcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return oldValue - 1;
|
||||||
|
}
|
||||||
|
|
||||||
template <class RuntimeClassFlagsT, bool implementsWeakReferenceSource, bool implementsFtmBase, typename ...TInterfaces>
|
template <class RuntimeClassFlagsT, bool implementsWeakReferenceSource, bool implementsFtmBase, typename ...TInterfaces>
|
||||||
class __declspec(novtable) RuntimeClassImpl<RuntimeClassFlagsT, implementsWeakReferenceSource, false, implementsFtmBase, TInterfaces...> :
|
class __declspec(novtable) RuntimeClassImpl<RuntimeClassFlagsT, implementsWeakReferenceSource, false, implementsFtmBase, TInterfaces...> :
|
||||||
public Details::AdjustImplements<RuntimeClassFlagsT, false, TInterfaces...>::Type,
|
public Details::AdjustImplements<RuntimeClassFlagsT, false, TInterfaces...>::Type,
|
||||||
|
@ -1624,7 +1655,7 @@ protected:
|
||||||
#ifdef _PERF_COUNTERS
|
#ifdef _PERF_COUNTERS
|
||||||
IncrementAddRefCount();
|
IncrementAddRefCount();
|
||||||
#endif
|
#endif
|
||||||
return UnknownIncrementReference(&refcount_);
|
return SafeUnknownIncrementReference(refcount_);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long InternalRelease() throw()
|
unsigned long InternalRelease() throw()
|
||||||
|
@ -1634,7 +1665,7 @@ protected:
|
||||||
#endif
|
#endif
|
||||||
// A release fence is required to ensure all guarded memory accesses are
|
// A release fence is required to ensure all guarded memory accesses are
|
||||||
// complete before any thread can begin destroying the object.
|
// complete before any thread can begin destroying the object.
|
||||||
unsigned long newValue = UnknownDecrementReference(&refcount_);
|
unsigned long newValue = SafeUnknownDecrementReference(refcount_);
|
||||||
if (newValue == 0)
|
if (newValue == 0)
|
||||||
{
|
{
|
||||||
// An acquire fence is required before object destruction to ensure
|
// An acquire fence is required before object destruction to ensure
|
||||||
|
@ -1786,7 +1817,7 @@ protected:
|
||||||
#ifdef _PERF_COUNTERS
|
#ifdef _PERF_COUNTERS
|
||||||
IncrementAddRefCount();
|
IncrementAddRefCount();
|
||||||
#endif
|
#endif
|
||||||
return UnknownIncrementReference(&refcount_);
|
return SafeUnknownIncrementReference(refcount_);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long InternalRelease() throw()
|
unsigned long InternalRelease() throw()
|
||||||
|
@ -1796,7 +1827,7 @@ protected:
|
||||||
#endif
|
#endif
|
||||||
// A release fence is required to ensure all guarded memory accesses are
|
// A release fence is required to ensure all guarded memory accesses are
|
||||||
// complete before any thread can begin destroying the object.
|
// complete before any thread can begin destroying the object.
|
||||||
unsigned long newValue = UnknownDecrementReference(&refcount_);
|
unsigned long newValue = SafeUnknownDecrementReference(refcount_);
|
||||||
if (newValue == 0)
|
if (newValue == 0)
|
||||||
{
|
{
|
||||||
// An acquire fence is required before object destruction to ensure
|
// An acquire fence is required before object destruction to ensure
|
||||||
|
@ -1828,14 +1859,14 @@ public:
|
||||||
|
|
||||||
unsigned long IncrementStrongReference() throw()
|
unsigned long IncrementStrongReference() throw()
|
||||||
{
|
{
|
||||||
return UnknownIncrementReference(&strongRefCount_);
|
return SafeUnknownIncrementReference(strongRefCount_);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long DecrementStrongReference() throw()
|
unsigned long DecrementStrongReference() throw()
|
||||||
{
|
{
|
||||||
// A release fence is required to ensure all guarded memory accesses are
|
// A release fence is required to ensure all guarded memory accesses are
|
||||||
// complete before any thread can begin destroying the object.
|
// complete before any thread can begin destroying the object.
|
||||||
unsigned long newValue = UnknownDecrementReference(&strongRefCount_);
|
unsigned long newValue = SafeUnknownDecrementReference(strongRefCount_);
|
||||||
if (newValue == 0)
|
if (newValue == 0)
|
||||||
{
|
{
|
||||||
// An acquire fence is required before object destruction to ensure
|
// An acquire fence is required before object destruction to ensure
|
||||||
|
@ -2089,7 +2120,7 @@ inline INT_PTR EncodeWeakReferencePointer(Microsoft::WRL::Details::WeakReference
|
||||||
|
|
||||||
inline Microsoft::WRL::Details::WeakReferenceImpl* DecodeWeakReferencePointer(INT_PTR value)
|
inline Microsoft::WRL::Details::WeakReferenceImpl* DecodeWeakReferencePointer(INT_PTR value)
|
||||||
{
|
{
|
||||||
return reinterpret_cast<Microsoft::WRL::Details::WeakReferenceImpl*>(value << 1);
|
return reinterpret_cast<Microsoft::WRL::Details::WeakReferenceImpl*>(static_cast<UINT_PTR>(value) << 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning(pop) // C6388
|
#pragma warning(pop) // C6388
|
||||||
|
@ -2134,6 +2165,7 @@ class RuntimeClass<InterfaceListHelper<TInterfaces...>, RuntimeClassFlagsT, impl
|
||||||
public RuntimeClassImpl<RuntimeClassFlagsT, implementsWeakReferenceSource, implementsInspectable, implementsFtmBase, TInterfaces...>
|
public RuntimeClassImpl<RuntimeClassFlagsT, implementsWeakReferenceSource, implementsInspectable, implementsFtmBase, TInterfaces...>
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
#pragma warning(suppress: 6101) // Function only used internally and the value of 'ppvObject' is only used if *handled is true
|
||||||
HRESULT CustomQueryInterface(REFIID /*riid*/, _Outptr_result_nullonfailure_ void** /*ppvObject*/, _Out_ bool *handled)
|
HRESULT CustomQueryInterface(REFIID /*riid*/, _Outptr_result_nullonfailure_ void** /*ppvObject*/, _Out_ bool *handled)
|
||||||
{
|
{
|
||||||
*handled = false;
|
*handled = false;
|
||||||
|
@ -2153,6 +2185,7 @@ class RuntimeClass :
|
||||||
RuntimeClass(const RuntimeClass&);
|
RuntimeClass(const RuntimeClass&);
|
||||||
RuntimeClass& operator=(const RuntimeClass&);
|
RuntimeClass& operator=(const RuntimeClass&);
|
||||||
protected:
|
protected:
|
||||||
|
#pragma warning(suppress: 6101) // Function only used internally and the value of 'ppvObject' is only used if *handled is true
|
||||||
HRESULT CustomQueryInterface(REFIID /*riid*/, _Outptr_result_nullonfailure_ void** /*ppvObject*/, _Out_ bool *handled)
|
HRESULT CustomQueryInterface(REFIID /*riid*/, _Outptr_result_nullonfailure_ void** /*ppvObject*/, _Out_ bool *handled)
|
||||||
{
|
{
|
||||||
*handled = false;
|
*handled = false;
|
||||||
|
@ -2177,6 +2210,7 @@ class RuntimeClass<RuntimeClassFlags<classFlags>, TInterfaces...> :
|
||||||
RuntimeClass(const RuntimeClass&);
|
RuntimeClass(const RuntimeClass&);
|
||||||
RuntimeClass& operator=(const RuntimeClass&);
|
RuntimeClass& operator=(const RuntimeClass&);
|
||||||
protected:
|
protected:
|
||||||
|
#pragma warning(suppress: 6101) // Function only used internally and the value of 'ppvObject' is only used if *handled is true
|
||||||
HRESULT CustomQueryInterface(REFIID /*riid*/, _Outptr_result_nullonfailure_ void** /*ppvObject*/, _Out_ bool *handled)
|
HRESULT CustomQueryInterface(REFIID /*riid*/, _Outptr_result_nullonfailure_ void** /*ppvObject*/, _Out_ bool *handled)
|
||||||
{
|
{
|
||||||
*handled = false;
|
*handled = false;
|
||||||
|
@ -2196,8 +2230,8 @@ public:
|
||||||
|
|
||||||
namespace Details
|
namespace Details
|
||||||
{
|
{
|
||||||
//Weak reference implementation
|
// Weak reference implementation
|
||||||
class WeakReferenceImpl sealed:
|
class WeakReferenceImpl final :
|
||||||
public ::Microsoft::WRL::RuntimeClass<RuntimeClassFlags<ClassicCom>, IWeakReference>,
|
public ::Microsoft::WRL::RuntimeClass<RuntimeClassFlags<ClassicCom>, IWeakReference>,
|
||||||
public StrongReference
|
public StrongReference
|
||||||
{
|
{
|
||||||
|
@ -2289,6 +2323,11 @@ unsigned long RuntimeClassImpl<RuntimeClassFlagsT, true, true, false, I0, TInter
|
||||||
{
|
{
|
||||||
if (!IsValueAPointerToWeakReference(currentValue.rawValue))
|
if (!IsValueAPointerToWeakReference(currentValue.rawValue))
|
||||||
{
|
{
|
||||||
|
if (static_cast<long>(currentValue.refCount) == LONG_MAX)
|
||||||
|
{
|
||||||
|
return LONG_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
UINT_PTR updateValue = currentValue.refCount + 1;
|
UINT_PTR updateValue = currentValue.refCount + 1;
|
||||||
|
|
||||||
#ifdef __WRL_UNITTEST__
|
#ifdef __WRL_UNITTEST__
|
||||||
|
@ -2324,6 +2363,11 @@ unsigned long RuntimeClassImpl<RuntimeClassFlagsT, true, true, false, I0, TInter
|
||||||
{
|
{
|
||||||
if (!IsValueAPointerToWeakReference(currentValue.rawValue))
|
if (!IsValueAPointerToWeakReference(currentValue.rawValue))
|
||||||
{
|
{
|
||||||
|
if (static_cast<long>(currentValue.refCount) == LONG_MAX)
|
||||||
|
{
|
||||||
|
return LONG_MAX - 1;
|
||||||
|
}
|
||||||
|
|
||||||
UINT_PTR updateValue = currentValue.refCount - 1;
|
UINT_PTR updateValue = currentValue.refCount - 1;
|
||||||
|
|
||||||
#ifdef __WRL_UNITTEST__
|
#ifdef __WRL_UNITTEST__
|
||||||
|
@ -2432,8 +2476,13 @@ public:
|
||||||
// Allocate memory with operator new(size, nothrow) only
|
// Allocate memory with operator new(size, nothrow) only
|
||||||
// This will allow developer to override one operator only
|
// This will allow developer to override one operator only
|
||||||
// to enable different memory allocation model
|
// to enable different memory allocation model
|
||||||
buffer_ = (char*) (operator new (sizeof(T), std::nothrow));
|
#ifdef __cpp_aligned_new
|
||||||
return buffer_;
|
if constexpr (alignof(T) > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
|
||||||
|
{
|
||||||
|
return buffer_ = (char*) operator new (sizeof(T), static_cast<std::align_val_t>(alignof(T)), ::std::nothrow);
|
||||||
|
}
|
||||||
|
#endif // /std:c++17 or later
|
||||||
|
return buffer_ = (char*) operator new (sizeof(T), ::std::nothrow);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Detach() throw()
|
void Detach() throw()
|
||||||
|
@ -2526,7 +2575,7 @@ namespace Details
|
||||||
{ \
|
{ \
|
||||||
return trustLevel; \
|
return trustLevel; \
|
||||||
} \
|
} \
|
||||||
STDMETHOD(GetRuntimeClassName)(_Out_ HSTRING* runtimeName) \
|
STDMETHOD(GetRuntimeClassName)(_Out_ HSTRING* runtimeName) override \
|
||||||
{ \
|
{ \
|
||||||
*runtimeName = nullptr; \
|
*runtimeName = nullptr; \
|
||||||
HRESULT hr = S_OK; \
|
HRESULT hr = S_OK; \
|
||||||
|
@ -2537,7 +2586,7 @@ namespace Details
|
||||||
} \
|
} \
|
||||||
return hr; \
|
return hr; \
|
||||||
} \
|
} \
|
||||||
STDMETHOD(GetTrustLevel)(_Out_ ::TrustLevel* trustLvl) \
|
STDMETHOD(GetTrustLevel)(_Out_ ::TrustLevel* trustLvl) override \
|
||||||
{ \
|
{ \
|
||||||
*trustLvl = trustLevel; \
|
*trustLvl = trustLevel; \
|
||||||
return S_OK; \
|
return S_OK; \
|
||||||
|
@ -2545,22 +2594,22 @@ namespace Details
|
||||||
STDMETHOD(GetIids)(_Out_ ULONG *iidCount, \
|
STDMETHOD(GetIids)(_Out_ ULONG *iidCount, \
|
||||||
_When_(*iidCount == 0, _At_(*iids, _Post_null_)) \
|
_When_(*iidCount == 0, _At_(*iids, _Post_null_)) \
|
||||||
_When_(*iidCount > 0, _At_(*iids, _Post_notnull_)) \
|
_When_(*iidCount > 0, _At_(*iids, _Post_notnull_)) \
|
||||||
_Result_nullonfailure_ IID **iids) \
|
_Result_nullonfailure_ IID **iids) override \
|
||||||
{ \
|
{ \
|
||||||
return RuntimeClassT::GetIids(iidCount, iids); \
|
return RuntimeClassT::GetIids(iidCount, iids); \
|
||||||
} \
|
} \
|
||||||
STDMETHOD(QueryInterface)(REFIID riid, _Outptr_result_nullonfailure_ void **ppvObject) \
|
STDMETHOD(QueryInterface)(REFIID riid, _Outptr_result_nullonfailure_ void **ppvObject) override \
|
||||||
{ \
|
{ \
|
||||||
bool handled = false; \
|
bool handled = false; \
|
||||||
HRESULT hr = this->CustomQueryInterface(riid, ppvObject, &handled); \
|
HRESULT hr = this->CustomQueryInterface(riid, ppvObject, &handled); \
|
||||||
if (FAILED(hr) || handled) return hr; \
|
if (FAILED(hr) || handled) return hr; \
|
||||||
return RuntimeClassT::QueryInterface(riid, ppvObject); \
|
return RuntimeClassT::QueryInterface(riid, ppvObject); \
|
||||||
} \
|
} \
|
||||||
STDMETHOD_(ULONG, Release)() \
|
STDMETHOD_(ULONG, Release)() override \
|
||||||
{ \
|
{ \
|
||||||
return RuntimeClassT::Release(); \
|
return RuntimeClassT::Release(); \
|
||||||
} \
|
} \
|
||||||
STDMETHOD_(ULONG, AddRef)() \
|
STDMETHOD_(ULONG, AddRef)() override \
|
||||||
{ \
|
{ \
|
||||||
return RuntimeClassT::AddRef(); \
|
return RuntimeClassT::AddRef(); \
|
||||||
} \
|
} \
|
||||||
|
@ -2648,8 +2697,4 @@ namespace Details
|
||||||
// Restore packing
|
// Restore packing
|
||||||
#include <poppack.h>
|
#include <poppack.h>
|
||||||
|
|
||||||
#ifdef __clang__
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // _WRL_IMPLEMENTS_H_
|
#endif // _WRL_IMPLEMENTS_H_
|
||||||
|
|
Loading…
Reference in New Issue