@@ -0,0 +1,93 @@
# Windows Implementation Libraries (WIL)

[![Build Status](https://dev.azure.com/msft-wil/Windows%20Implementation%20Library/_apis/build/status/Microsoft.wil?branchName=master)](https://dev.azure.com/msft-wil/Windows%20Implementation%20Library/_build/latest?definitionId=1&branchName=master)

The Windows Implementation Libraries (WIL) is a header-only C++ library created to make life easier
for developers on Windows through readable type-safe C++ interfaces for common Windows coding patterns.

Some things that WIL includes to whet your appetite:

- [`include/wil/resource.h`](include/wil/resource.h)
([documentation](https://github.com/Microsoft/wil/wiki/RAII-resource-wrappers)):
Smart pointers and auto-releasing resource wrappers to let you manage Windows
API HANDLEs, HWNDs, and other resources and resource handles with
[RAII](https://en.cppreference.com/w/cpp/language/raii) semantics.
- [`include/wil/win32_helpers.h`](include/wil/win32_helpers.h): Wrappers for API functions
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,
casting or converting between types, and so on.
- [`include/wil/registry.h`](include/wil/registry.h): Registry watchers that can
call a lambda function or callback you provide whenever a certain tree within
the Windows registry changes.
- [`include/wil/result.h`](include/wil/result.h)
([documentation](https://github.com/Microsoft/wil/wiki/Error-handling-helpers)):
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
error codes or C++ exceptions in your code.

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,
and some (such as the RAII resource wrappers) can even be used in kernel mode.

# Documentation

This project is documented in [its GitHub wiki](https://github.com/Microsoft/wil/wiki). Feel free to contribute to it!

# Consuming WIL
WIL follows the "live at head" philosophy, so you should feel free to consume WIL directly from the GitHub repo however you please: as a GIT submodule, symbolic link, download and copy files, etc. and update to the latest version at your own cadence. Alternatively, WIL is available using a few package managers, mentioned below. These packages will be updated periodically, likely to average around once or twice per month.

## Consuming WIL via NuGet
WIL is available on nuget.org under the name [Microsoft.Windows.ImplementationLibrary](https://www.nuget.org/packages/Microsoft.Windows.ImplementationLibrary/). This package includes the header files under the [include](include) directory as well as a [.targets](packaging/nuget/Microsoft.Windows.ImplementationLibrary.targets) file.

## Consuming WIL via vcpkg
WIL is also available using [vcpkg](https://github.com/microsoft/vcpkg) under the name [wil](https://github.com/microsoft/vcpkg/blob/master/ports/wil/portfile.cmake). Instructions for installing packages can be found in the [vcpkg GitHub docs](https://github.com/microsoft/vcpkg/blob/master/docs/examples/installing-and-using-packages.md). In general, once vcpkg is set up on the system, you can run:
```cmd
C:\vcpkg> vcpkg install wil:x86-windows
C:\vcpkg> vcpkg install wil:x64-windows
```
Note that even though WIL is a header-only library, you still need to install the package for all architectures/platforms you wish to use it with. Otherwise, WIL won't be added to the include path for the missing architectures/platforms. Execute `vcpkg help triplet` for a list of available options.

# 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
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:
```cmd
C:\wil> scripts\init.cmd -c clang -g ninja -b debug
```
You can execute `init.cmd --help` for a summary of available options. The scripts use a common directory pattern of `build/$(compiler)$(arch)$(type)` for the build output root. E.g. `build/clang64debug` when using Clang as the compiler, x64 as the architecture, and Debug as the build type. It is this directory where you will want to build from. For example, if you initialized using the command above, you can build the tests like so:
```cmd
C:\wil\build\clang64debug> ninja
```
Or, if you want to only build a single test (e.g. for improved compile times):
```cmd
C:\wil\build\clang64debug> ninja witest.noexcept
```
If you initialized using MSBuild as the generator, there will be a `.sln` file in the root of the build directory. You
can either open the solution in Visual Studio or invoke MSBuild directly to build.

The output is a number of test executables. If you used the initialization script(s) mentioned above, or if you followed
the same directory naming convention of those scripts, you can use the [runtests.cmd](scripts/runtests.cmd) script,
which will execute any test executables that have been built, erroring out - and preserving the exit code - if any test
fails. Note that MSBuild will modify the output directory names, so this script is only compatible with using Ninja as the
generator. If you are at the tail end of of a change, you can execute the following to get a wide range of coverage:
```cmd
C:\wil> scripts\init_all.cmd
C:\wil> scripts\build_all.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
repeat this process for the other architecture (e.g. by using the "x86 Native Tools Command Prompt for VS 2019")

# Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.

When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.

This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
@@ -0,0 +1,60 @@
THIRD PARTY SOFTWARE NOTICES AND INFORMATION
Do Not Translate or Localize

This software incorporates material from third parties. Microsoft makes certain open source code available at http://3rdpartysource.microsoft.com, or you may send a check or money order for US $5.00, including the product name, the open source component name, and version number, to:

Source Code Compliance Team
Microsoft Corporation
One Microsoft Way
Redmond, WA 98052
USA

Notwithstanding any other terms, you may reverse engineer this software to the extent required to debug changes to any libraries licensed under the GNU Lesser General Public License.

Libc++

Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

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. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

Catch2

Boost Software License - Version 1.0 - August 17th, 2003

Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:

The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.

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, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,69 @@

# E.g. replace_cxx_flag("/W[0-4]", "/W4")
macro(replace_cxx_flag pattern text)
foreach (flag
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)

string(REGEX REPLACE "${pattern}" "${text}" ${flag} "${${flag}}")

endforeach()
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

# Be as strict as reasonably possible, since we want to support consumers using strict warning levels
replace_cxx_flag("/W[0-4]" "/W4")
append_cxx_flag("/WX")

# We want to be as conformant as possible, so tell MSVC to not be permissive (note that this has no effect on clang-cl)
append_cxx_flag("/permissive-")

# wistd::function has padding due to alignment. This is expected
append_cxx_flag("/wd4324")

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
append_cxx_flag("-Wno-self-assign-overloaded")
append_cxx_flag("-Wno-self-move")

# clang-cl does not understand the /permissive- flag (or at least it opts to ignore it). We can achieve similar
# results through the following flags.
append_cxx_flag("-fno-delayed-template-parsing")

# 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
# append_cxx_flag("-fno-ms-compatibility")
# append_cxx_flag("-ferror-limit=999")
# append_cxx_flag("-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)
# append_cxx_flag("-Xclang -std=c++2a")
else()
# Flags that are either ignored or unrecognized by clang-cl
# TODO: https://github.com/Microsoft/wil/issues/6
# append_cxx_flag("/experimental:preprocessor")

# CRT headers are not yet /experimental:preprocessor clean, so work around the known issues
# append_cxx_flag("/Wv:18")

append_cxx_flag("/bigobj")

# NOTE: Temporary workaround while https://github.com/microsoft/wil/issues/102 is being investigated
append_cxx_flag("/d2FH4-")
endif()

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -0,0 +1,251 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_CPPWINRT_INCLUDED
#define __WIL_CPPWINRT_INCLUDED

#include "common.h"
#include <windows.h>
#include <unknwn.h>
#include <hstring.h>

// WIL and C++/WinRT use two different exception types for communicating HRESULT failures. Thus, both libraries need to
// understand how to translate these exception types into the correct HRESULT values at the ABI boundary. Prior to
// C++/WinRT "2.0" this was accomplished by injecting the WINRT_EXTERNAL_CATCH_CLAUSE macro - that WIL defines below -
// into its exception handler (winrt::to_hresult). Starting with C++/WinRT "2.0" this mechanism has shifted to a global
// function pointer - winrt_to_hresult_handler - that WIL sets automatically when this header is included and
// 'CPPWINRT_SUPPRESS_STATIC_INITIALIZERS' is not defined.

/// @cond
namespace wil::details
{
// Since the C++/WinRT version macro is a string...
inline constexpr int major_version_from_string(const char* versionString)
{
int result = 0;
auto str = versionString;
while ((*str >= '0') && (*str <= '9'))
{
result = result * 10 + (*str - '0');
++str;
}

return result;
}
}
/// @endcond

#ifdef CPPWINRT_VERSION
// Prior to C++/WinRT "2.0" this header needed to be included before 'winrt/base.h' so that our definition of
// 'WINRT_EXTERNAL_CATCH_CLAUSE' would get picked up in the implementation of 'winrt::to_hresult'. This is no longer
// problematic, so only emit an error when using a version of C++/WinRT prior to 2.0
static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2,
"Please include wil/cppwinrt.h before including any C++/WinRT headers");
#endif

// NOTE: Will eventually be removed once C++/WinRT 2.0 use can be assumed
#ifdef WINRT_EXTERNAL_CATCH_CLAUSE
#define __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE 1
#else
#define WINRT_EXTERNAL_CATCH_CLAUSE \
catch (const wil::ResultException& e) \
{ \
return winrt::hresult_error(e.GetErrorCode(), winrt::to_hstring(e.what())).to_abi(); \
}
#endif

#include "result_macros.h"
#include <winrt/base.h>

#if __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE
static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2,
"C++/WinRT external catch clause already defined outside of WIL");
#endif

// In C++/WinRT 2.0 and beyond, this function pointer exists. In earlier versions it does not. It's much easier to avoid
// linker errors than it is to SFINAE on variable existence, so we declare the variable here, but are careful not to
// use it unless the version of C++/WinRT is high enough
extern std::int32_t(__stdcall* winrt_to_hresult_handler)(void*) noexcept;

/// @cond
namespace wil::details
{
inline void MaybeGetExceptionString(
const winrt::hresult_error& exception,
_Out_writes_opt_(debugStringChars) PWSTR debugString,
_When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars)
{
if (debugString)
{
StringCchPrintfW(debugString, debugStringChars, L"winrt::hresult_error: %ls", exception.message().c_str());
}
}

inline HRESULT __stdcall ResultFromCaughtException_CppWinRt(
_Inout_updates_opt_(debugStringChars) PWSTR debugString,
_When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars,
_Inout_ bool* isNormalized) noexcept
{
if (g_pfnResultFromCaughtException)
{
try
{
throw;
}
catch (const ResultException& exception)
{
*isNormalized = true;
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.GetErrorCode();
}
catch (const winrt::hresult_error& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.code().value;
}
catch (const std::bad_alloc& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_OUTOFMEMORY;
}
catch (const std::out_of_range& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_BOUNDS;
}
catch (const std::invalid_argument& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_INVALIDARG;
}
catch (...)
{
auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars);
if (FAILED(hr))
{
return hr;
}
}
}
else
{
try
{
throw;
}
catch (const ResultException& exception)
{
*isNormalized = true;
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.GetErrorCode();
}
catch (const winrt::hresult_error& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return exception.code().value;
}
catch (const std::bad_alloc& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_OUTOFMEMORY;
}
catch (const std::out_of_range& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_BOUNDS;
}
catch (const std::invalid_argument& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return E_INVALIDARG;
}
catch (const std::exception& exception)
{
MaybeGetExceptionString(exception, debugString, debugStringChars);
return HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION);
}
catch (...)
{
// Fall through to returning 'S_OK' below
}
}

// Tell the caller that we were unable to map the exception by succeeding...
return S_OK;
}
}
/// @endcond

namespace wil
{
inline std::int32_t __stdcall winrt_to_hresult(void* returnAddress) noexcept
{
// C++/WinRT only gives us the return address (caller), so pass along an empty 'DiagnosticsInfo' since we don't
// have accurate file/line/etc. information
return static_cast<std::int32_t>(details::ReportFailure_CaughtException<FailureType::Return>(__R_DIAGNOSTICS_RA(DiagnosticsInfo{}, returnAddress)));
}

inline void WilInitialize_CppWinRT()
{
details::g_pfnResultFromCaughtException_CppWinRt = details::ResultFromCaughtException_CppWinRt;
if constexpr (details::major_version_from_string(CPPWINRT_VERSION) >= 2)
{
WI_ASSERT(winrt_to_hresult_handler == nullptr);
winrt_to_hresult_handler = winrt_to_hresult;
}
}

/// @cond
namespace details
{
#ifndef CPPWINRT_SUPPRESS_STATIC_INITIALIZERS
WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "0")
WI_HEADER_INITITALIZATION_FUNCTION(WilInitialize_CppWinRT, []
{
::wil::WilInitialize_CppWinRT();
return 1;
});
#else
WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "1")
#endif
}
/// @endcond

// Provides an overload of verify_hresult so that the WIL macros can recognize winrt::hresult as a valid "hresult" type.
inline long verify_hresult(winrt::hresult hr) noexcept
{
return hr;
}

// Provides versions of get_abi and put_abi for genericity that directly use HSTRING for convenience.
template <typename T>
auto get_abi(T const& object) noexcept
{
return winrt::get_abi(object);
}

inline auto get_abi(winrt::hstring const& object) noexcept
{
return static_cast<HSTRING>(winrt::get_abi(object));
}

template <typename T>
auto put_abi(T& object) noexcept
{
return winrt::put_abi(object);
}

inline auto put_abi(winrt::hstring& object) noexcept
{
return reinterpret_cast<HSTRING*>(winrt::put_abi(object));
}
}

#endif // __WIL_CPPWINRT_INCLUDED

Large diffs are not rendered by default.

@@ -0,0 +1,277 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_REGISTRY_INCLUDED
#define __WIL_REGISTRY_INCLUDED

#ifdef _KERNEL_MODE
#error This header is not supported in kernel-mode.
#endif

#include <winreg.h>
#include <new.h> // new(std::nothrow)
#include "resource.h" // unique_hkey

namespace wil
{
//! The key name includes the absolute path of the key in the registry, always starting at a
//! base key, for example, HKEY_LOCAL_MACHINE.
size_t const max_registry_key_name_length = 255;

//! The maximum number of characters allowed in a registry value's name.
size_t const max_registry_value_name_length = 16383;

// unique_registry_watcher/unique_registry_watcher_nothrow/unique_registry_watcher_failfast
// These classes make it easy to execute a provided function when a
// registry key changes (optionally recursively). Specify the key
// either as a root key + path, or an open registry handle as wil::unique_hkey
// or a raw HKEY value (that will be duplicated).
//
// Example use with exceptions base error handling:
// auto watcher = wil::make_registry_watcher(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind changeKind[]
// {
// if (changeKind == RegistryChangeKind::Delete)
// {
// watcher.reset();
// }
// // invalidate cached registry data here
// });
//
// Example use with error code base error handling:
// auto watcher = wil::make_registry_watcher_nothrow(HKEY_CURRENT_USER, L"Software\\MyApp", true, wil::RegistryChangeKind[]
// {
// // invalidate cached registry data here
// });
// RETURN_IF_NULL_ALLOC(watcher);

enum class RegistryChangeKind
{
Modify = 0,
Delete = 1,
};

/// @cond
namespace details
{
struct registry_watcher_state
{
registry_watcher_state(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
: m_callback(wistd::move(callback)), m_keyToWatch(wistd::move(keyToWatch)), m_isRecursive(isRecursive)
{
}
wistd::function<void(RegistryChangeKind)> m_callback;
unique_hkey m_keyToWatch;
unique_event_nothrow m_eventHandle;

// While not strictly needed since this is ref counted the thread pool wait
// should be last to ensure that the other members are valid
// when it is destructed as it will reference them.
unique_threadpool_wait m_threadPoolWait;
bool m_isRecursive;

volatile long m_refCount = 1;
srwlock m_lock;

// Returns true if the refcount can be increased from a non zero value,
// false it was zero impling that the object is in or on the way to the destructor.
// In this case ReleaseFromCallback() should not be called.
bool TryAddRef()
{
return ::InterlockedIncrement(&m_refCount) > 1;
}

void Release()
{
auto lock = m_lock.lock_exclusive();
if (0 == ::InterlockedDecrement(&m_refCount))
{
lock.reset(); // leave the lock before deleting it.
delete this;
}
}

void ReleaseFromCallback(bool rearm)
{
auto lock = m_lock.lock_exclusive();
if (0 == ::InterlockedDecrement(&m_refCount))
{
// Destroy the thread pool wait now to avoid the wait that would occur in the
// destructor. That wait would cause a deadlock since we are doing this from the callback.
::CloseThreadpoolWait(m_threadPoolWait.release());
lock.reset(); // leave the lock before deleting it.
delete this;
// Sleep(1); // Enable for testing to find use after free bugs.
}
else if (rearm)
{
::SetThreadpoolWait(m_threadPoolWait.get(), m_eventHandle.get(), nullptr);
}
}
};

inline void delete_registry_watcher_state(_In_opt_ registry_watcher_state *watcherStorage) { watcherStorage->Release(); }

typedef resource_policy<registry_watcher_state *, decltype(&details::delete_registry_watcher_state),
details::delete_registry_watcher_state, details::pointer_access_none> registry_watcher_state_resource_policy;
}
/// @endcond

template <typename storage_t, typename err_policy = err_exception_policy>
class registry_watcher_t : public storage_t
{
public:
// forward all base class constructors...
template <typename... args_t>
explicit registry_watcher_t(args_t&&... args) WI_NOEXCEPT : storage_t(wistd::forward<args_t>(args)...) {}

// HRESULT or void error handling...
typedef typename err_policy::result result;

// Exception-based constructors
registry_watcher_t(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
create(rootKey, subKey, isRecursive, wistd::move(callback));
}

registry_watcher_t(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
static_assert(wistd::is_same<void, result>::value, "this constructor requires exceptions; use the create method");
create(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
}

// Pass a root key, sub key pair or use an empty string to use rootKey as the key to watch.
result create(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
// Most use will want to create the key, consider adding an option for open as a future design change.
unique_hkey keyToWatch;
HRESULT hr = HRESULT_FROM_WIN32(RegCreateKeyExW(rootKey, subKey, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToWatch, nullptr));
if (FAILED(hr))
{
return err_policy::HResult(hr);
}
return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
}

result create(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
return err_policy::HResult(create_common(wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
}

private:
// Factored into a standalone function to support Clang which does not support conversion of stateless lambdas
// to __stdcall
static void __stdcall callback(PTP_CALLBACK_INSTANCE, void *context, TP_WAIT *, TP_WAIT_RESULT)
{
#ifndef __WIL_REGISTRY_CHANGE_CALLBACK_TEST
#define __WIL_REGISTRY_CHANGE_CALLBACK_TEST
#endif
__WIL_REGISTRY_CHANGE_CALLBACK_TEST
auto watcherState = static_cast<details::registry_watcher_state *>(context);
if (watcherState->TryAddRef())
{
// using auto reset event so don't need to manually reset.

// failure here is a programming error.
const LSTATUS error = RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(), watcherState->m_isRecursive,
REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC,
watcherState->m_eventHandle.get(), TRUE);

// Call the client before re-arming to ensure that multiple callbacks don't
// run concurrently.
switch (error)
{
case ERROR_SUCCESS:
case ERROR_ACCESS_DENIED:
// Normal modification: send RegistryChangeKind::Modify and re-arm.
watcherState->m_callback(RegistryChangeKind::Modify);
watcherState->ReleaseFromCallback(true);
break;

case ERROR_KEY_DELETED:
// Key deleted, send RegistryChangeKind::Delete, do not re-arm.
watcherState->m_callback(RegistryChangeKind::Delete);
watcherState->ReleaseFromCallback(false);
break;

case ERROR_HANDLE_REVOKED:
// Handle revoked. This can occur if the user session ends before
// the watcher shuts-down. Disarm silently since there is generally no way to respond.
watcherState->ReleaseFromCallback(false);
break;

default:
FAIL_FAST_HR(HRESULT_FROM_WIN32(error));
}
}
}

// This function exists to avoid template expansion of this code based on err_policy.
HRESULT create_common(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
wistd::unique_ptr<details::registry_watcher_state> watcherState(new(std::nothrow) details::registry_watcher_state(
wistd::move(keyToWatch), isRecursive, wistd::move(callback)));
RETURN_IF_NULL_ALLOC(watcherState);
RETURN_IF_FAILED(watcherState->m_eventHandle.create());
RETURN_IF_WIN32_ERROR(RegNotifyChangeKeyValue(watcherState->m_keyToWatch.get(),
watcherState->m_isRecursive, REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_THREAD_AGNOSTIC,
watcherState->m_eventHandle.get(), TRUE));

watcherState->m_threadPoolWait.reset(CreateThreadpoolWait(&registry_watcher_t::callback, watcherState.get(), nullptr));
RETURN_LAST_ERROR_IF(!watcherState->m_threadPoolWait);
storage_t::reset(watcherState.release()); // no more failures after this, pass ownership
SetThreadpoolWait(storage_t::get()->m_threadPoolWait.get(), storage_t::get()->m_eventHandle.get(), nullptr);
return S_OK;
}
};

typedef unique_any_t<registry_watcher_t<details::unique_storage<details::registry_watcher_state_resource_policy>, err_returncode_policy>> unique_registry_watcher_nothrow;
typedef unique_any_t<registry_watcher_t<details::unique_storage<details::registry_watcher_state_resource_policy>, err_failfast_policy>> unique_registry_watcher_failfast;

inline unique_registry_watcher_nothrow make_registry_watcher_nothrow(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback) WI_NOEXCEPT
{
unique_registry_watcher_nothrow watcher;
watcher.create(rootKey, subKey, isRecursive, wistd::move(callback));
return watcher; // caller must test for success using if (watcher)
}

inline unique_registry_watcher_nothrow make_registry_watcher_nothrow(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback) WI_NOEXCEPT
{
unique_registry_watcher_nothrow watcher;
watcher.create(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
return watcher; // caller must test for success using if (watcher)
}

inline unique_registry_watcher_failfast make_registry_watcher_failfast(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
return unique_registry_watcher_failfast(rootKey, subKey, isRecursive, wistd::move(callback));
}

inline unique_registry_watcher_failfast make_registry_watcher_failfast(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
return unique_registry_watcher_failfast(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
}

#ifdef WIL_ENABLE_EXCEPTIONS
typedef unique_any_t<registry_watcher_t<details::unique_storage<details::registry_watcher_state_resource_policy>, err_exception_policy >> unique_registry_watcher;

inline unique_registry_watcher make_registry_watcher(HKEY rootKey, _In_ PCWSTR subKey, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
return unique_registry_watcher(rootKey, subKey, isRecursive, wistd::move(callback));
}

inline unique_registry_watcher make_registry_watcher(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
{
return unique_registry_watcher(wistd::move(keyToWatch), isRecursive, wistd::move(callback));
}
#endif // WIL_ENABLE_EXCEPTIONS
} // namespace wil

#endif

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -0,0 +1,96 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************

// Note: When origination is enabled by including this file, origination is done as part of the RETURN_* and THROW_* macros. Before originating
// a new error we will observe whether there is already an error payload associated with the current thread. If there is, and the HRESULTs match,
// then a new error will not be originated. Otherwise we will overwrite it with a new origination. The ABI boundary for WinRT APIs will check the
// per-thread error information. The act of checking the error clears it, so there should be minimal risk of failing to originate distinct errors
// simply because the HRESULTs match.
//
// For THROW_ macros we will examine the thread-local error storage once per throw. So typically once, with additional calls if the exception is
// caught and re-thrown.
//
// For RETURN_ macros we will have to examine the thread-local error storage once per frame as the call stack unwinds. Because error conditions
// -should- be uncommon the performance impact of checking TLS should be minimal. The more expensive part is originating the error because it must
// capture the entire stack and some additional data.

#ifndef __WIL_RESULT_ORIGINATE_INCLUDED
#define __WIL_RESULT_ORIGINATE_INCLUDED

#include "result.h"
#include <OleAuto.h> // RestrictedErrorInfo uses BSTRs :(
#include "resource.h"
#include "com.h"
#include <roerrorapi.h>

#ifndef __cplusplus_winrt // The CX runtime likes to originate errors already so we would conflict with them.
namespace wil
{
namespace details
{
// Note: The name must begin with "Raise" so that the !analyze auto-bucketing will ignore this stack frame. Otherwise this line of code gets all the blame.
inline void __stdcall RaiseRoOriginateOnWilExceptions(wil::FailureInfo const& failure) WI_NOEXCEPT
{
if ((failure.type == FailureType::Return) || (failure.type == FailureType::Exception))
{
bool shouldOriginate = true;

wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK)
{
// This thread already has an error origination payload. Don't originate again if it has the same HRESULT that we are
// observing right now.
wil::unique_bstr descriptionUnused;
HRESULT existingHr = failure.hr;
wil::unique_bstr restrictedDescriptionUnused;
wil::unique_bstr capabilitySidUnused;
if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)))
{
shouldOriginate = (failure.hr != existingHr);
}
}

if (shouldOriginate)
{
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
wil::unique_hmodule errorModule;
if (GetModuleHandleExW(0, L"api-ms-win-core-winrt-error-l1-1-1.dll", &errorModule))
{
auto pfn = reinterpret_cast<decltype(&::RoOriginateError)>(GetProcAddress(errorModule.get(), "RoOriginateError"));
if (pfn != nullptr)
{
pfn(failure.hr, nullptr);
}
}
#else // DESKTOP | SYSTEM
::RoOriginateError(failure.hr, nullptr);
#endif // DESKTOP | SYSTEM
}
else if (restrictedErrorInformation)
{
// GetRestrictedErrorInfo returns ownership of the error information. If we aren't originating, and an error was already present,
// then we need to restore the error information for later observation.
SetRestrictedErrorInfo(restrictedErrorInformation.get());
}
}
}
} // namespace details
} // namespace wil

// Automatically call RoOriginateError upon error origination by including this file
WI_HEADER_INITITALIZATION_FUNCTION(ResultStowedExceptionInitialize, []
{
::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions);
return 1;
});
#endif // __cplusplus_winrt

#endif // __WIL_RESULT_ORIGINATE_INCLUDED
@@ -0,0 +1,206 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_RPC_HELPERS_INCLUDED
#define __WIL_RPC_HELPERS_INCLUDED

#include "result.h"
#include "resource.h"
#include "wistd_functional.h"
#include "wistd_type_traits.h"

namespace wil
{

/// @cond
namespace details
{
// This call-adapter template converts a void-returning 'wistd::invoke' into
// an HRESULT-returning 'wistd::invoke' that emits S_OK. It can be eliminated
// with 'if constexpr' when C++17 is in wide use.
template<typename TReturnType> struct call_adapter
{
template<typename... TArgs> static HRESULT call(TArgs&& ... args)
{
return wistd::invoke(wistd::forward<TArgs>(args)...);
}
};

template<> struct call_adapter<void>
{
template<typename... TArgs> static HRESULT call(TArgs&& ... args)
{
wistd::invoke(wistd::forward<TArgs>(args)...);
return S_OK;
}
};

// Some RPC exceptions are already HRESULTs. Others are in the regular Win32
// error space. If the incoming exception code isn't an HRESULT, wrap it.
constexpr HRESULT map_rpc_exception(DWORD code)
{
return IS_ERROR(code) ? code : __HRESULT_FROM_WIN32(code);
}
}
/// @endcond

/** Invokes an RPC method, mapping structured exceptions to HRESULTs
Failures encountered by the RPC infrastructure (such as server crashes, authentication
errors, client parameter issues, etc.) are emitted by raising a structured exception from
within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept,
RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual
flow control machinery to use.
Many RPC methods are defined as returning HRESULT themselves, where the HRESULT indicates
the result of the _work_. HRESULTs returned by a successful completion of the _call_ are
returned as-is.
RPC methods that have a return type of 'void' are mapped to returning S_OK when the _call_
completes successfully.
For example, consider an RPC interface method defined in idl as:
~~~
HRESULT GetKittenState([in, ref, string] const wchar_t* name, [out, retval] KittenState** state);
~~~
To call this method, use:
~~~
wil::unique_rpc_binding binding = // typically gotten elsewhere;
wil::unique_midl_ptr<KittenState> state;
HRESULT hr = wil::invoke_rpc_nothrow(GetKittenState, binding.get(), L"fluffy", state.put());
RETURN_IF_FAILED(hr);
~~~
*/
template<typename... TCall> HRESULT invoke_rpc_nothrow(TCall&&... args) WI_NOEXCEPT
{
RpcTryExcept
{
// Note: this helper type can be removed with C++17 enabled via
// 'if constexpr(wistd::is_same_v<void, result_t>)'
using result_t = typename wistd::__invoke_of<TCall...>::type;
RETURN_IF_FAILED(details::call_adapter<result_t>::call(wistd::forward<TCall>(args)...));
return S_OK;
}
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
{
RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
}
RpcEndExcept
}

/** Invokes an RPC method, mapping structured exceptions to HRESULTs
Failures encountered by the RPC infrastructure (such as server crashes, authentication
errors, client parameter issues, etc.) are emitted by raising a structured exception from
within the RPC machinery. This method wraps the requested call in the usual RpcTryExcept,
RpcTryCatch, and RpcEndExcept sequence then maps the exceptions to HRESULTs for the usual
flow control machinery to use.
Some RPC methods return results (such as a state enumeration or other value) directly in
their signature. This adapter writes that result into a caller-provided object then
returns S_OK.
For example, consider an RPC interface method defined in idl as:
~~~
GUID GetKittenId([in, ref, string] const wchar_t* name);
~~~
To call this method, use:
~~~
wil::unique_rpc_binding binding = // typically gotten elsewhere;
GUID id;
HRESULT hr = wil::invoke_rpc_result_nothrow(id, GetKittenId, binding.get(), L"fluffy");
RETURN_IF_FAILED(hr);
~~~
*/
template<typename TResult, typename... TCall> HRESULT invoke_rpc_result_nothrow(TResult& result, TCall&&... args) WI_NOEXCEPT
{
RpcTryExcept
{
result = wistd::invoke(wistd::forward<TCall>(args)...);
return S_OK;
}
RpcExcept(RpcExceptionFilter(RpcExceptionCode()))
{
RETURN_HR(details::map_rpc_exception(RpcExceptionCode()));
}
RpcEndExcept
}

namespace details
{
// Provides an adapter around calling the context-handle-close method on an
// RPC interface, which itself is an RPC call.
template<typename TStorage, typename close_fn_t, close_fn_t close_fn>
struct rpc_closer_t
{
static void Close(TStorage arg) WI_NOEXCEPT
{
LOG_IF_FAILED(invoke_rpc_nothrow(close_fn, &arg));
}
};
}

/** Manages explicit RPC context handles
Explicit RPC context handles are used in many RPC interfaces. Most interfaces with
context handles have an explicit `FooClose([in, out] CONTEXT*)` method that lets
the server close out the context handle. As the close method itself is an RPC call,
it can fail and raise a structured exception.
This type routes the context-handle-specific `Close` call through the `invoke_rpc_nothrow`
helper, ensuring correct cleanup and lifecycle management.
~~~
// Assume the interface has two methods:
// HRESULT OpenFoo([in] handle_t binding, [out] FOO_CONTEXT*);
// HRESULT UseFoo([in] FOO_CONTEXT context;
// void CloseFoo([in, out] PFOO_CONTEXT);
using unique_foo_context = wil::unique_rpc_context_handle<FOO_CONTEXT, decltype(&CloseFoo), CloseFoo>;
unique_foo_context context;
RETURN_IF_FAILED(wil::invoke_rpc_nothrow(OpenFoo, m_binding.get(), context.put()));
RETURN_IF_FAILED(wil::invoke_rpc_nothrow(UseFoo, context.get()));
context.reset();
~~~
*/
template<typename TContext, typename close_fn_t, close_fn_t close_fn>
using unique_rpc_context_handle = unique_any<TContext, decltype(&details::rpc_closer_t<TContext, close_fn_t, close_fn>::Close), details::rpc_closer_t<TContext, close_fn_t, close_fn>::Close>;

#ifdef WIL_ENABLE_EXCEPTIONS
/** Invokes an RPC method, mapping structured exceptions to C++ exceptions
See `wil::invoke_rpc_nothrow` for additional information. Failures during the _call_
and those returned by the _method_ are mapped to HRESULTs and thrown inside a
wil::ResultException. Using the example RPC method provided above:
~~~
wil::unique_midl_ptr<KittenState> state;
wil::invoke_rpc(GetKittenState, binding.get(), L"fluffy", state.put());
// use 'state'
~~~
*/
template<typename... TCall> void invoke_rpc(TCall&& ... args)
{
THROW_IF_FAILED(invoke_rpc_nothrow(wistd::forward<TCall>(args)...));
}

/** Invokes an RPC method, mapping structured exceptions to C++ exceptions
See `wil::invoke_rpc_result_nothrow` for additional information. Failures during the
_call_ are mapped to HRESULTs and thrown inside a `wil::ResultException`. Using the
example RPC method provided above:
~~~
GUID id = wil::invoke_rpc_result(GetKittenId, binding.get());
// use 'id'
~~~
*/
template<typename... TCall> auto invoke_rpc_result(TCall&& ... args)
{
using result_t = typename wistd::__invoke_of<TCall...>::type;
result_t result{};
THROW_IF_FAILED(invoke_rpc_result_nothrow(result, wistd::forward<TCall>(args)...));
return result;
}
#endif
}

#endif

Large diffs are not rendered by default.

@@ -0,0 +1,124 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_STL_INCLUDED
#define __WIL_STL_INCLUDED

#include "common.h"
#include "resource.h"
#include <memory>
#include <string>

#if defined(WIL_ENABLE_EXCEPTIONS)
namespace std
{
template<class _Ty, class _Alloc>
class vector;

template<class _Elem>
struct char_traits;

template<class _Elem, class _Traits, class _Alloc>
class basic_string;
} // namespace std

namespace wil
{
/** Secure allocator for STL containers.
The `wil::secure_allocator` allocator calls `SecureZeroMemory` before deallocating
memory. This provides a mechanism for secure STL containers such as `wil::secure_vector`,
`wil::secure_string`, and `wil::secure_wstring`. */
template <typename T>
struct secure_allocator
: public std::allocator<T>
{
template<typename Other>
struct rebind
{
typedef secure_allocator<Other> other;
};

secure_allocator()
: std::allocator<T>()
{
}

~secure_allocator() = default;

secure_allocator(const secure_allocator& a)
: std::allocator<T>(a)
{
}

template <class U>
secure_allocator(const secure_allocator<U>& a)
: std::allocator<T>(a)
{
}

T* allocate(size_t n)
{
return std::allocator<T>::allocate(n);
}

void deallocate(T* p, size_t n)
{
SecureZeroMemory(p, sizeof(T) * n);
std::allocator<T>::deallocate(p, n);
}
};

//! `wil::secure_vector` will be securely zeroed before deallocation.
template <typename Type>
using secure_vector = std::vector<Type, secure_allocator<Type>>;
//! `wil::secure_wstring` will be securely zeroed before deallocation.
using secure_wstring = std::basic_string<wchar_t, std::char_traits<wchar_t>, wil::secure_allocator<wchar_t>>;
//! `wil::secure_string` will be securely zeroed before deallocation.
using secure_string = std::basic_string<char, std::char_traits<char>, wil::secure_allocator<char>>;

/// @cond
namespace details
{
template<> struct string_maker<std::wstring>
{
HRESULT make(_In_reads_opt_(length) PCWSTR source, size_t length) WI_NOEXCEPT try
{
m_value = source ? std::wstring(source, length) : std::wstring(length, L'\0');
return S_OK;
}
catch (...)
{
return E_OUTOFMEMORY;
}

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

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

static PCWSTR get(const std::wstring& value) { return value.c_str(); }

private:
std::wstring m_value;
};
}
/// @endcond

// str_raw_ptr is an overloaded function that retrieves a const pointer to the first character in a string's buffer.
// This is the overload for std::wstring. Other overloads available in resource.h.
inline PCWSTR str_raw_ptr(const std::wstring& str)
{
return str.c_str();
}

} // namespace wil

#endif // WIL_ENABLE_EXCEPTIONS

#endif // __WIL_STL_INCLUDED

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

@@ -0,0 +1,84 @@
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
//
//*********************************************************
#ifndef __WIL_WRL_INCLUDED
#define __WIL_WRL_INCLUDED

#include <wrl.h>
#include "result.h"
#include "common.h" // wistd type_traits helpers

namespace wil
{

#ifdef WIL_ENABLE_EXCEPTIONS
#pragma region Object construction helpers that throw exceptions

/** Used to construct a RuntimeClass based object that uses 2 phase construction.
Construct a RuntimeClass based object that uses 2 phase construction (by implementing
RuntimeClassInitialize() and returning error codes for failures.
~~~~
// SomeClass uses 2 phase initialization by implementing RuntimeClassInitialize()
auto someClass = MakeAndInitializeOrThrow<SomeClass>(L"input", true);
~~~~ */

template <typename T, typename... TArgs>
Microsoft::WRL::ComPtr<T> MakeAndInitializeOrThrow(TArgs&&... args)
{
Microsoft::WRL::ComPtr<T> obj;
THROW_IF_FAILED(Microsoft::WRL::MakeAndInitialize<T>(&obj, Microsoft::WRL::Details::Forward<TArgs>(args)...));
return obj;
}

/** Used to construct an RuntimeClass based object that uses exceptions in its constructor (and does
not require 2 phase construction).
~~~~
// SomeClass uses exceptions for error handling in its constructor.
auto someClass = MakeOrThrow<SomeClass>(L"input", true);
~~~~ */

template <typename T, typename... TArgs>
Microsoft::WRL::ComPtr<T> MakeOrThrow(TArgs&&... args)
{
// This is how you can detect the presence of RuntimeClassInitialize() and find dangerous use.
// Unfortunately this produces false positives as all RuntimeClass derived classes have
// a RuntimeClassInitialize() method from their base class.
// static_assert(!std::is_member_function_pointer<decltype(&T::RuntimeClassInitialize)>::value,
// "class has a RuntimeClassInitialize member, use MakeAndInitializeOrThrow instead");
auto obj = Microsoft::WRL::Make<T>(Microsoft::WRL::Details::Forward<TArgs>(args)...);
THROW_IF_NULL_ALLOC(obj.Get());
return obj;
}
#pragma endregion

#endif // WIL_ENABLE_EXCEPTIONS

/** By default WRL Callback objects are not agile, use this to make an agile one. Replace use of Callback<> with MakeAgileCallback<>.
Will return null on failure, translate that into E_OUTOFMEMORY using XXX_IF_NULL_ALLOC()
from wil\result.h to test the result. */
template<typename TDelegateInterface, typename ...Args>
::Microsoft::WRL::ComPtr<TDelegateInterface> MakeAgileCallbackNoThrow(Args&&... args) WI_NOEXCEPT
{
using namespace Microsoft::WRL;
return Callback<Implements<RuntimeClassFlags<ClassicCom>, TDelegateInterface, FtmBase>>(wistd::forward<Args>(args)...);
}

#ifdef WIL_ENABLE_EXCEPTIONS
template<typename TDelegateInterface, typename ...Args>
::Microsoft::WRL::ComPtr<TDelegateInterface> MakeAgileCallback(Args&&... args)
{
auto result = MakeAgileCallbackNoThrow<TDelegateInterface, Args...>(wistd::forward<Args>(args)...);
THROW_IF_NULL_ALLOC(result);
return result;
}
#endif // WIL_ENABLE_EXCEPTIONS
} // namespace wil

#endif // __WIL_WRL_INCLUDED
@@ -0,0 +1,2 @@

add_subdirectory(nuget)
@@ -0,0 +1,20 @@

file(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}/build_tools/nuget.exe" nuget_exe)
file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}" nupkg_dir)
file(TO_NATIVE_PATH "${nupkg_dir}/Microsoft.Windows.ImplementationLibrary.${WIL_BUILD_VERSION}.nupkg" wil_nupkg)

# The build servers don't have an up-to-date version of nuget, so pull it down ourselves...
file(DOWNLOAD https://dist.nuget.org/win-x86-commandline/latest/nuget.exe ${nuget_exe})

file(GLOB_RECURSE wil_headers ${CMAKE_SOURCE_DIR}/include/*.h)

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
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.nuspec
${CMAKE_CURRENT_SOURCE_DIR}/Microsoft.Windows.ImplementationLibrary.targets
${wil_headers}
${CMAKE_SOURCE_DIR}/LICENSE
${CMAKE_SOURCE_DIR}/ThirdPartyNotices.txt)

add_custom_target(make_wil_nupkg DEPENDS ${wil_nupkg})
@@ -0,0 +1,21 @@
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>Microsoft.Windows.ImplementationLibrary</id>
<version>0.0.0</version>
<title>Windows Implementation Library</title>
<authors>Microsoft</authors>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>The Windows Implementation Libraries (wil) were created to improve productivity and solve problems commonly seen by Windows developers.</description>
<tags>windows utility wil native</tags>
<copyright>© Microsoft Corporation. All rights reserved.</copyright>
<license type="file">LICENSE</license>
<projectUrl>https://github.com/Microsoft/wil</projectUrl>
</metadata>
<files>
<file src="..\..\LICENSE"/>
<file src="..\..\ThirdPartyNotices.txt"/>
<file src="..\..\include\**" target="include\" />
<file src="Microsoft.Windows.ImplementationLibrary.targets" target="build\native\" />
</files>
</package>
@@ -0,0 +1,8 @@
<?xml version="1.0"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>$(MSBuildThisFileDirectory)..\..\include\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
</Project>
@@ -0,0 +1,41 @@
# Windows Implementation Library Pipeline

trigger:
- master

jobs:
- job: BuildAndTest
timeoutInMinutes: 360

pool:
vmImage: 'windows-2019'

steps:
- script: |
choco install llvm
if %ERRORLEVEL% NEQ 0 goto :eof
echo ##vso[task.setvariable variable=PATH]%PATH%;C:\Program Files\LLVM\bin
displayName: 'Install Clang'
- script: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars32.bat"
if %ERRORLEVEL% NEQ 0 goto :eof
call scripts\init_all.cmd --fast
if %ERRORLEVEL% NEQ 0 goto :eof
call scripts\build_all.cmd
displayName: 'Build x86'
- script: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
if %ERRORLEVEL% NEQ 0 goto :eof
call scripts\init_all.cmd --fast
if %ERRORLEVEL% NEQ 0 goto :eof
call scripts\build_all.cmd
displayName: 'Build x64'
- script: call scripts\runtests.cmd
displayName: 'Run Tests'
@@ -0,0 +1,51 @@
@echo off
setlocal EnableDelayedExpansion

set BUILD_ROOT=%~dp0\..\build

if "%Platform%"=="x64" (
set BUILD_ARCH=64
) else if "%Platform%"=="x86" (
set BUILD_ARCH=32
) else if [%Platform%]==[] (
echo ERROR: The build_all.cmd script must be run from a Visual Studio command window
exit /B 1
) else (
echo ERROR: Unrecognized/unsupported platform %Platform%
exit /B 1
)

call :build clang debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
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
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :build msvc release
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!

goto :eof

:: build [compiler] [type]
:build
set BUILD_DIR=%BUILD_ROOT%\%1%BUILD_ARCH%%2
if not exist %BUILD_DIR% (
goto :eof
)

pushd %BUILD_DIR%
echo Building from %CD%
ninja
popd
goto :eof
@@ -0,0 +1,202 @@
@echo off
setlocal
setlocal EnableDelayedExpansion

:: Globals
set BUILD_ROOT=%~dp0\..\build

goto :init

:usage
echo USAGE:
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 [--fast] [-v^|--version X.Y.Z]
echo.
echo ARGUMENTS
echo -c^|--compiler Controls the compiler used, either 'clang' (the default) or 'msvc'
echo -g^|--generator Controls the CMake generator used, either 'ninja' (the default) or 'msbuild'
echo -b^|--build-type Controls the value of 'CMAKE_BUILD_TYPE', either 'debug' (the default), 'release',
echo 'relwithdebinfo', or 'minsizerel'
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 --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 temporary and will be removed once https://github.com/microsoft/wil/issues/9 is fixed
goto :eof

:init
:: Initialize values as empty so that we can identify if we are using defaults later for error purposes
set COMPILER=
set GENERATOR=
set BUILD_TYPE=
set LINKER=
set CMAKE_ARGS=
set BITNESS=
set VERSION=
set FAST_BUILD=0

:parse
if /I "%~1"=="" goto :execute

if /I "%~1"=="--help" call :usage & goto :eof

set COMPILER_SET=0
if /I "%~1"=="-c" set COMPILER_SET=1
if /I "%~1"=="--compiler" set COMPILER_SET=1
if %COMPILER_SET%==1 (
if "%COMPILER%" NEQ "" echo ERROR: Compiler already specified & call :usage & exit /B 1

if /I "%~2"=="clang" set COMPILER=clang
if /I "%~2"=="msvc" set COMPILER=msvc
if "!COMPILER!"=="" echo ERROR: Unrecognized/missing compiler %~2 & call :usage & exit /B 1

shift
shift
goto :parse
)

set GENERATOR_SET=0
if /I "%~1"=="-g" set GENERATOR_SET=1
if /I "%~1"=="--generator" set GENERATOR_SET=1
if %GENERATOR_SET%==1 (
if "%GENERATOR%" NEQ "" echo ERROR: Generator already specified & call :usage & exit /B 1

if /I "%~2"=="ninja" set GENERATOR=ninja
if /I "%~2"=="msbuild" set GENERATOR=msbuild
if "!GENERATOR!"=="" echo ERROR: Unrecognized/missing generator %~2 & call :usage & exit /B 1

shift
shift
goto :parse
)

set BUILD_TYPE_SET=0
if /I "%~1"=="-b" set BUILD_TYPE_SET=1
if /I "%~1"=="--build-type" set BUILD_TYPE_SET=1
if %BUILD_TYPE_SET%==1 (
if "%BUILD_TYPE%" NEQ "" echo ERROR: Build type already specified & call :usage & exit /B 1

if /I "%~2"=="debug" set BUILD_TYPE=debug
if /I "%~2"=="release" set BUILD_TYPE=release
if /I "%~2"=="relwithdebinfo" set BUILD_TYPE=relwithdebinfo
if /I "%~2"=="minsizerel" set BUILD_TYPE=minsizerel
if "!BUILD_TYPE!"=="" echo ERROR: Unrecognized/missing build type %~2 & call :usage & exit /B 1

shift
shift
goto :parse
)

set LINKER_SET=0
if /I "%~1"=="-l" set LINKER_SET=1
if /I "%~1"=="--linker" set LINKER_SET=1
if %LINKER_SET%==1 (
if "%LINKER%" NEQ "" echo ERROR: Linker already specified & call :usage & exit /B 1

if /I "%~2"=="link" set LINKER=link
if /I "%~2"=="lld-link" set LINKER=lld-link
if "!LINKER!"=="" echo ERROR: Unrecognized/missing linker %~2 & call :usage & exit /B 1

shift
shift
goto :parse
)

set VERSION_SET=0
if /I "%~1"=="-v" set VERSION_SET=1
if /I "%~1"=="--version" set VERSION_SET=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

shift
shift
goto :parse
)

if /I "%~1"=="--fast" (
if %FAST_BUILD% NEQ 0 echo ERROR: Fast build already specified
set FAST_BUILD=1
shift
goto :parse
)

echo ERROR: Unrecognized argument %~1
call :usage
exit /B 1

:execute
:: Check for conflicting arguments
if "%GENERATOR%"=="msbuild" (
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
if "%GENERATOR%"=="" set GENERATOR=ninja
if %GENERATOR%==msbuild set COMPILER=msvc

if "%COMPILER%"=="" set COMPILER=clang

if "%BUILD_TYPE%"=="" set BUILD_TYPE=debug

if "%LINKER%"=="" set LINKER=link

:: Formulate CMake arguments
if %GENERATOR%==ninja set CMAKE_ARGS=%CMAKE_ARGS% -G Ninja

if %COMPILER%==clang set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
if %COMPILER%==msvc set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl

if %GENERATOR% NEQ msbuild (
if %BUILD_TYPE%==debug set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=Debug
if %BUILD_TYPE%==release set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=Release
if %BUILD_TYPE%==relwithdebinfo set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=RelWithDebInfo
if %BUILD_TYPE%==minsizerel set CMAKE_ARGS=%CMAKE_ARGS% -DCMAKE_BUILD_TYPE=MinSizeRel
) else (
:: The Visual Studio generator, by default, will use the most recent Windows SDK version installed that is not
:: greater than the host OS version. This decision is to ensure that anything built will be able to run on the
:: machine that built it. This experience is generally okay, if not desired, but affects us since we build with
:: '/permissive-' etc. and older versions of the SDK are typically not as clean as the most recent versions.
:: This flag will force the generator to select the most recent SDK version independent of host OS version.
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 %FAST_BUILD%==1 set CMAKE_ARGS=%CMAKE_ARGS% -DFAST_BUILD=ON

:: 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%"=="x86" (
set BITNESS=32
if %COMPILER%==clang set CFLAGS=-m32 & set CXXFLAGS=-m32
)
if "%Platform%"=="x64" set BITNESS=64
if "%BITNESS%"=="" echo ERROR: Unrecognized/unsupported platform %Platform% & exit /B 1

:: Set up the build directory
set BUILD_DIR=%BUILD_ROOT%\%COMPILER%%BITNESS%%BUILD_TYPE%
mkdir %BUILD_DIR% > NUL 2>&1

:: Run CMake
pushd %BUILD_DIR%
echo Using compiler....... %COMPILER%
echo Using linker......... %LINKER%
echo Using architecture... %Platform%
echo Using build type..... %BUILD_TYPE%
echo Using build root..... %CD%
echo.
cmake %CMAKE_ARGS% ..\..
popd

goto :eof
@@ -0,0 +1,13 @@
@echo off

:: NOTE: Architecture is picked up from the command window, so we can't control that here :(

call %~dp0\init.cmd -c clang -g ninja -b debug %*
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call %~dp0\init.cmd -c clang -g ninja -b relwithdebinfo %*
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 )
@@ -0,0 +1,67 @@
@echo off
setlocal EnableDelayedExpansion

set BUILD_ROOT=%~dp0\..\build

:: Unlike building, we don't need to limit ourselves to the Platform of the command window
call :execute_tests clang64debug
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang64release
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
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang32release
if %ERRORLEVEL% NEQ 0 ( goto :eof )
call :execute_tests clang32relwithdebinfo
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

:execute_tests
set BUILD_DIR=%BUILD_ROOT%\%1
if not exist %BUILD_DIR% ( goto :eof )

pushd %BUILD_DIR%
echo Running tests from %CD%
call :execute_test app witest.app.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
call :execute_test cpplatest witest.cpplatest.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
call :execute_test noexcept witest.noexcept.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
call :execute_test normal witest.exe
if %ERRORLEVEL% NEQ 0 ( popd && goto :eof )
popd

goto :eof

:execute_test
if not exist tests\%1\%2 ( goto :eof )
echo Running %1 tests...
tests\%1\%2
goto :eof
@@ -0,0 +1,19 @@

include(${CMAKE_SOURCE_DIR}/cmake/common_build_flags.cmake)

# All projects need to reference the WIL headers
include_directories(${CMAKE_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
include_directories(BEFORE SYSTEM ./workarounds/wrl)

# The build pipelines have limitations that local development environments do not, so turn a few knobs
if (${FAST_BUILD})
replace_cxx_flag("/GR" "/GR-") # Disables RTTI
add_definitions(-DCATCH_CONFIG_FAST_COMPILE -DWIL_FAST_BUILD)
endif()

add_subdirectory(app)
add_subdirectory(cpplatest)
add_subdirectory(noexcept)
add_subdirectory(normal)

Large diffs are not rendered by default.

@@ -0,0 +1,243 @@

#include <wil/common.h>
#include <wil/resource.h>
#include <wrl/client.h>

#include "common.h"

TEST_CASE("CommonTests::OutParamHelpers", "[common]")
{
int i = 2;
int *pOutTest = &i;
int *pNullTest = nullptr;

SECTION("Value type")
{
wil::assign_to_opt_param(pNullTest, 3);
wil::assign_to_opt_param(pOutTest, 3);
REQUIRE(*pOutTest == 3);
}

SECTION("Pointer to value type")
{
int **ppOutTest = &pOutTest;
int **ppNullTest = nullptr;
wil::assign_null_to_opt_param(ppNullTest);
wil::assign_null_to_opt_param(ppOutTest);
REQUIRE(*ppOutTest == nullptr);
}

SECTION("COM out pointer")
{
Microsoft::WRL::ComPtr<IUnknown> spUnk;
IUnknown **ppunkNull = nullptr;
IUnknown *pUnk = reinterpret_cast<IUnknown *>(1);
IUnknown **ppUnkValid = &pUnk;

wil::detach_to_opt_param(ppunkNull, spUnk);
wil::detach_to_opt_param(ppUnkValid, spUnk);
REQUIRE(*ppUnkValid == nullptr);
}
}

TEST_CASE("CommonTests::TypeValidation", "[common]")
{
std::unique_ptr<BYTE> boolCastClass;
std::vector<int> noBoolCastClass;
HRESULT hr = S_OK;
BOOL bigBool = true;
bool smallBool = true;
DWORD dword = 1;
Microsoft::WRL::ComPtr<IUnknown> comPtr;
(void)dword;

// NOTE: The commented out verify* calls should give compilation errors
SECTION("verify_bool")
{
REQUIRE(wil::verify_bool(smallBool));
REQUIRE(wil::verify_bool(bigBool));
REQUIRE_FALSE(wil::verify_bool(boolCastClass));
REQUIRE_FALSE(wil::verify_bool(comPtr));
//wil::verify_bool(noBoolCastClass);
//wil::verify_bool(dword);
//wil::verify_bool(hr);
}

SECTION("verify_hresult")
{
//wil::verify_hresult(smallBool);
//wil::verify_hresult(bigBool);
//wil::verify_hresult(boolCastClass);
//wil::verify_hresult(noBoolCastClass);
//wil::verify_hresult(dword);
//wil::verify_hresult(comPtr);
REQUIRE(wil::verify_hresult(hr) == S_OK);
}

SECTION("verify_BOOL")
{
//wil::verify_BOOL(smallBool);
REQUIRE(wil::verify_BOOL(bigBool));
//wil::verify_BOOL(boolCastClass);
//wil::verify_BOOL(noBoolCastClass);
//wil::verify_BOOL(dword);
//wil::verify_BOOL(comPtr);
//wil::verify_BOOL(hr);
}
}

template <typename T>
static void FlagsMacrosNonStatic(T none, T one, T two, T three, T four)
{
T eval = one | four;

REQUIRE(WI_AreAllFlagsSet(MDEC(eval), MDEC(one | four)));
REQUIRE_FALSE(WI_AreAllFlagsSet(eval, one | three));
REQUIRE_FALSE(WI_AreAllFlagsSet(eval, three | two));
REQUIRE(WI_AreAllFlagsSet(eval, none));

REQUIRE(WI_IsAnyFlagSet(MDEC(eval), MDEC(one)));
REQUIRE(WI_IsAnyFlagSet(eval, one | four | three));
REQUIRE_FALSE(WI_IsAnyFlagSet(eval, two));

REQUIRE(WI_AreAllFlagsClear(MDEC(eval), MDEC(three)));
REQUIRE(WI_AreAllFlagsClear(eval, three | two));
REQUIRE_FALSE(WI_AreAllFlagsClear(eval, one | four));
REQUIRE_FALSE(WI_AreAllFlagsClear(eval, one | three));

REQUIRE(WI_IsAnyFlagClear(MDEC(eval), MDEC(three)));
REQUIRE(WI_IsAnyFlagClear(eval, three | two));
REQUIRE(WI_IsAnyFlagClear(eval, four | three));
REQUIRE_FALSE(WI_IsAnyFlagClear(eval, one));
REQUIRE_FALSE(WI_IsAnyFlagClear(eval, one | four));

REQUIRE_FALSE(WI_IsSingleFlagSet(MDEC(eval)));
REQUIRE(WI_IsSingleFlagSet(eval & one));

REQUIRE(WI_IsSingleFlagSetInMask(MDEC(eval), MDEC(one)));
REQUIRE(WI_IsSingleFlagSetInMask(eval, one | three));
REQUIRE_FALSE(WI_IsSingleFlagSetInMask(eval, three));
REQUIRE_FALSE(WI_IsSingleFlagSetInMask(eval, one | four));

REQUIRE_FALSE(WI_IsClearOrSingleFlagSet(MDEC(eval)));
REQUIRE(WI_IsClearOrSingleFlagSet(eval & one));
REQUIRE(WI_IsClearOrSingleFlagSet(none));

REQUIRE(WI_IsClearOrSingleFlagSetInMask(MDEC(eval), MDEC(one)));
REQUIRE(WI_IsClearOrSingleFlagSetInMask(eval, one | three));
REQUIRE(WI_IsClearOrSingleFlagSetInMask(eval, three));
REQUIRE_FALSE(WI_IsClearOrSingleFlagSetInMask(eval, one | four));

eval = none;
WI_SetAllFlags(MDEC(eval), MDEC(one));
REQUIRE(eval == one);
WI_SetAllFlags(eval, one | two);
REQUIRE(eval == (one | two));

eval = one | two;
WI_ClearAllFlags(MDEC(eval), one);
REQUIRE(eval == two);
WI_ClearAllFlags(eval, two);
REQUIRE(eval == none);

eval = one | two;
WI_UpdateFlagsInMask(MDEC(eval), MDEC(two | three), MDEC(three | four));
REQUIRE(eval == (one | three));

eval = one;
WI_ToggleAllFlags(MDEC(eval), MDEC(one | two));
REQUIRE(eval == two);
}

enum class EClassTest
{
None = 0x0,
One = 0x1,
Two = 0x2,
Three = 0x4,
Four = 0x8,
};
DEFINE_ENUM_FLAG_OPERATORS(EClassTest);

enum ERawTest
{
ER_None = 0x0,
ER_One = 0x1,
ER_Two = 0x2,
ER_Three = 0x4,
ER_Four = 0x8,
};
DEFINE_ENUM_FLAG_OPERATORS(ERawTest);

TEST_CASE("CommonTests::FlagsMacros", "[common]")
{
SECTION("Integral types")
{
FlagsMacrosNonStatic<char>(static_cast<char>(0), static_cast<char>(0x1), static_cast<char>(0x2), static_cast<char>(0x4), static_cast<char>(0x40));
FlagsMacrosNonStatic<unsigned char>(0, 0x1, 0x2, 0x4, 0x80u);
FlagsMacrosNonStatic<short>(0, 0x1, 0x2, 0x4, 0x4000);
FlagsMacrosNonStatic<unsigned short>(0, 0x1, 0x2, 0x4, 0x8000u);
FlagsMacrosNonStatic<long>(0, 0x1, 0x2, 0x4, 0x80000000ul);
FlagsMacrosNonStatic<unsigned long>(0, 0x1, 0x2, 0x4, 0x80000000ul);
FlagsMacrosNonStatic<long long>(0, 0x1, 0x2, 0x4, 0x8000000000000000ull);
FlagsMacrosNonStatic<unsigned long long>(0, 0x1, 0x2, 0x4, 0x8000000000000000ull);
}

SECTION("Raw enum")
{
FlagsMacrosNonStatic<ERawTest>(ER_None, ER_One, ER_Two, ER_Three, ER_Four);
}

SECTION("Enum class")
{
FlagsMacrosNonStatic<EClassTest>(EClassTest::None, EClassTest::One, EClassTest::Two, EClassTest::Three, EClassTest::Four);

EClassTest eclass = EClassTest::One | EClassTest::Two;
REQUIRE(WI_IsFlagSet(MDEC(eclass), EClassTest::One));
REQUIRE(WI_IsFlagSet(eclass, EClassTest::Two));
REQUIRE_FALSE(WI_IsFlagSet(eclass, EClassTest::Three));

REQUIRE(WI_IsFlagClear(MDEC(eclass), EClassTest::Three));
REQUIRE_FALSE(WI_IsFlagClear(eclass, EClassTest::One));

REQUIRE_FALSE(WI_IsSingleFlagSet(MDEC(eclass)));
REQUIRE(WI_IsSingleFlagSet(eclass & EClassTest::One));

eclass = EClassTest::None;
WI_SetFlag(MDEC(eclass), EClassTest::One);
REQUIRE(eclass == EClassTest::One);

eclass = EClassTest::None;
WI_SetFlagIf(eclass, EClassTest::One, false);
REQUIRE(eclass == EClassTest::None);
WI_SetFlagIf(eclass, EClassTest::One, true);
REQUIRE(eclass == EClassTest::One);

eclass = EClassTest::None;
WI_SetFlagIf(eclass, EClassTest::One, false);
REQUIRE(eclass == EClassTest::None);
WI_SetFlagIf(eclass, EClassTest::One, true);
REQUIRE(eclass == EClassTest::One);

eclass = EClassTest::One | EClassTest::Two;
WI_ClearFlag(eclass, EClassTest::Two);
REQUIRE(eclass == EClassTest::One);

eclass = EClassTest::One | EClassTest::Two;
WI_ClearFlagIf(eclass, EClassTest::One, false);
REQUIRE(eclass == (EClassTest::One | EClassTest::Two));
WI_ClearFlagIf(eclass, EClassTest::One, true);
REQUIRE(eclass == EClassTest::Two);

eclass = EClassTest::None;
WI_UpdateFlag(eclass, EClassTest::One, true);
REQUIRE(eclass == EClassTest::One);
WI_UpdateFlag(eclass, EClassTest::One, false);
REQUIRE(eclass == EClassTest::None);

eclass = EClassTest::One;
WI_ToggleFlag(eclass, EClassTest::One);
WI_ToggleFlag(eclass, EClassTest::Two);
REQUIRE(eclass == EClassTest::Two);
}
}
@@ -0,0 +1,28 @@

// Prior to C++/WinRT 2.0 this would cause issues since we're not including wil/cppwinrt.h in this translation unit.
// 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

#include <winrt/base.h>
#include <wil/result.h>

#include "common.h"

TEST_CASE("CppWinRTTests::CppWinRT20Test", "[cppwinrt]")
{
auto test = [](HRESULT hr)
{
try
{
THROW_HR(hr);
}
catch (...)
{
REQUIRE(hr == winrt::to_hresult());
}
};

test(E_OUTOFMEMORY);
test(E_INVALIDARG);
test(E_UNEXPECTED);
}
@@ -0,0 +1,144 @@

#include <wil/cppwinrt.h>

#include "catch.hpp"

// HRESULT values that C++/WinRT throws as something other than winrt::hresult_error - e.g. a type derived from
// winrt::hresult_error, std::*, etc.
static const HRESULT cppwinrt_mapped_hresults[] =
{
E_ACCESSDENIED,
RPC_E_WRONG_THREAD,
E_NOTIMPL,
E_INVALIDARG,
E_BOUNDS,
E_NOINTERFACE,
CLASS_E_CLASSNOTAVAILABLE,
E_CHANGED_STATE,
E_ILLEGAL_METHOD_CALL,
E_ILLEGAL_STATE_CHANGE,
E_ILLEGAL_DELEGATE_ASSIGNMENT,
HRESULT_FROM_WIN32(ERROR_CANCELLED),
E_OUTOFMEMORY,
};

TEST_CASE("CppWinRTTests::WilToCppWinRTExceptionTranslationTest", "[cppwinrt]")
{
auto test = [](HRESULT hr)
{
try
{
THROW_HR(hr);
}
catch (...)
{
REQUIRE(hr == winrt::to_hresult());
}
};

for (auto hr : cppwinrt_mapped_hresults)
{
test(hr);
}

// A non-mapped HRESULT
test(E_UNEXPECTED);
}

TEST_CASE("CppWinRTTests::CppWinRTToWilExceptionTranslationTest", "[cppwinrt]")
{
auto test = [](HRESULT hr)
{
try
{
winrt::check_hresult(hr);
}
catch (...)
{
REQUIRE(hr == wil::ResultFromCaughtException());
}
};

for (auto hr : cppwinrt_mapped_hresults)
{
test(hr);
}

// A non-mapped HRESULT
test(E_UNEXPECTED);
}

TEST_CASE("CppWinRTTests::ResultFromExceptionDebugTest", "[cppwinrt]")
{
auto test = [](HRESULT hr, wil::SupportedExceptions supportedExceptions)
{
auto result = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, supportedExceptions, [&]()
{
winrt::check_hresult(hr);
});
REQUIRE(hr == result);
};

for (auto hr : cppwinrt_mapped_hresults)
{
test(hr, wil::SupportedExceptions::Known);
test(hr, wil::SupportedExceptions::All);
}

// A non-mapped HRESULT
test(E_UNEXPECTED, wil::SupportedExceptions::Known);
test(E_UNEXPECTED, wil::SupportedExceptions::All);

// Uncomment any of the following to validate SEH failfast
//test(E_UNEXPECTED, wil::SupportedExceptions::None);
//test(E_ACCESSDENIED, wil::SupportedExceptions::Thrown);
//test(E_INVALIDARG, wil::SupportedExceptions::ThrownOrAlloc);
}

TEST_CASE("CppWinRTTests::CppWinRTConsistencyTest", "[cppwinrt]")
{
// Since setting 'winrt_to_hresult_handler' opts us into _all_ C++/WinRT exception translation handling, we need to
// make sure that we preserve behavior, at least with 'check_hresult', especially when C++/WinRT maps a particular
// HRESULT value to a different exception type
auto test = [](HRESULT hr)
{
try
{
winrt::check_hresult(hr);
}
catch (...)
{
REQUIRE(hr == winrt::to_hresult());
}
};

for (auto hr : cppwinrt_mapped_hresults)
{
test(hr);
}

// A non-mapped HRESULT
test(E_UNEXPECTED);

// C++/WinRT also maps a few std::* exceptions to various HRESULTs. We should preserve this behavior
try
{
throw std::out_of_range("oopsie");
}
catch (...)
{
REQUIRE(winrt::to_hresult() == E_BOUNDS);
}

try
{
throw std::invalid_argument("daisy");
}
catch (...)
{
REQUIRE(winrt::to_hresult() == E_INVALIDARG);
}

// 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)
}

Large diffs are not rendered by default.