Add tests and other related things needed to get CI builds working (#11)
* Starting on CMake * Start adding tests * Yay, tests * Stashing for now; need to full update the headers * Update headers * Last of the tests * Noexcept tests! * Build/conformance/etc/etc * Add cppwinrt tests * Exit code for scripts * Will this compile now? * Configurable CI build options * oops * Work around sadness * minor update * Hmm * Just testing... * Conditionally enable test * Add nuspec * hmm * Add targets file * Move back to license file to see what was wrong with it * Take 2? * Build nupkg with CMake since build servers have an old nuget version * Update scripts for build machine purposes * Case insensitive compare * I've made this change so many times now... * Revert str_raw_ptr change & add more in depth tests * Don't build nuget by default * Turns out reliance on path was a bad thing... * Some cleanup
This commit is contained in:
Родитель
5a6e8df546
Коммит
fbcd1d2abb
|
@ -331,3 +331,6 @@ ASALocalRun/
|
|||
|
||||
# Visual Studio Code directory
|
||||
.vscode/
|
||||
|
||||
# CMake/Build output
|
||||
build/
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
cmake_minimum_required(VERSION 3.11)
|
||||
|
||||
# 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)
|
||||
|
||||
if (NOT DEFINED WIL_BUILD_VERSION)
|
||||
set(WIL_BUILD_VERSION "0.0.0")
|
||||
endif()
|
||||
|
||||
add_subdirectory(packaging)
|
||||
add_subdirectory(tests)
|
|
@ -32,3 +32,29 @@ 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,62 @@
|
|||
|
||||
# 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-invalid-noreturn")
|
||||
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")
|
||||
|
||||
# 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.
|
||||
# TODO: https://github.com/Microsoft/wil/issues/10 - not yet clean enough to have this on by default
|
||||
# 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")
|
||||
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")
|
||||
endif()
|
|
@ -1,4 +1,13 @@
|
|||
|
||||
//*********************************************************
|
||||
//
|
||||
// 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_INCLUDED
|
||||
#define __WIL_COM_INCLUDED
|
||||
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#ifndef __WIL_COMMON_INCLUDED
|
||||
#define __WIL_COMMON_INCLUDED
|
||||
|
||||
|
@ -165,25 +174,6 @@ Three exception modes are available:
|
|||
//! This macro is for use in other macros to paste two tokens together, such as a constant and the __LINE__ macro.
|
||||
#define WI_PASTE(a, b) __WI_PASTE_imp(a, b)
|
||||
|
||||
//! This macro is used to invoke another function-like macro with the specified argument list. This is primarily useful
|
||||
//! when the invocation does not begin with a '(' character, e.g. when using the `WI_FLATTEN` macro. For example:
|
||||
//! ~~~
|
||||
//! #define CALL_WITH_BAR_SUFFIX(prefix, ...) WI_PASTE(prefix, Bar) WI_FLATTEN((__VA_ARGS__))
|
||||
//!
|
||||
//! // The macro invocation gets expanded to 'FooBar(x)' and does not get processed any further
|
||||
//! #define FooBar(x) foo_bar(x)
|
||||
//! CALL_WITH_BAR_SUFFIX(Foo, x);
|
||||
//! ~~~
|
||||
//! Instead, you should do:
|
||||
//! ~~~
|
||||
//! #define CALL_WITH_BAR_SUFFIX(prefix, ...) WI_MACRO_INVOKE(WI_PASTE(prefix, Bar), WI_FLATTEN((__VA_ARGS__)))
|
||||
//!
|
||||
//! // The macro invocation gets expanded to 'foo_bar(x)'
|
||||
//! #define FooBar(x) foo_bar(x)
|
||||
//! CALL_WITH_BAR_SUFFIX(Foo, x);
|
||||
//! ~~~
|
||||
#define WI_MACRO_INVOKE(fn, argsList) fn argsList
|
||||
|
||||
/// @cond
|
||||
#define __WI_ARGS_COUNT1(A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17, A18, A19, A20, A21, A22, A23, A24, A25, A26, A27, A28, A29, \
|
||||
A30, A31, A32, A33, A34, A35, A36, A37, A38, A39, A40, A41, A42, A43, A44, A45, A46, A47, A48, A49, A50, A51, A52, A53, A54, A55, A56, A57, A58, A59, \
|
||||
|
@ -200,108 +190,108 @@ Three exception modes are available:
|
|||
|
||||
/// @cond
|
||||
#define __WI_FOR_imp0( fn)
|
||||
#define __WI_FOR_imp1( fn, arg, ...) __WI_FOR_impN( 0, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp2( fn, arg, ...) __WI_FOR_impN( 1, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp3( fn, arg, ...) __WI_FOR_impN( 2, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp4( fn, arg, ...) __WI_FOR_impN( 3, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp5( fn, arg, ...) __WI_FOR_impN( 4, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp6( fn, arg, ...) __WI_FOR_impN( 5, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp7( fn, arg, ...) __WI_FOR_impN( 6, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp8( fn, arg, ...) __WI_FOR_impN( 7, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp9( fn, arg, ...) __WI_FOR_impN( 8, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp10(fn, arg, ...) __WI_FOR_impN( 9, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp11(fn, arg, ...) __WI_FOR_impN(10, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp12(fn, arg, ...) __WI_FOR_impN(11, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp13(fn, arg, ...) __WI_FOR_impN(12, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp14(fn, arg, ...) __WI_FOR_impN(13, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp15(fn, arg, ...) __WI_FOR_impN(14, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp16(fn, arg, ...) __WI_FOR_impN(15, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp17(fn, arg, ...) __WI_FOR_impN(16, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp18(fn, arg, ...) __WI_FOR_impN(17, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp19(fn, arg, ...) __WI_FOR_impN(18, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp20(fn, arg, ...) __WI_FOR_impN(19, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp21(fn, arg, ...) __WI_FOR_impN(20, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp22(fn, arg, ...) __WI_FOR_impN(21, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp23(fn, arg, ...) __WI_FOR_impN(22, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp24(fn, arg, ...) __WI_FOR_impN(23, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp25(fn, arg, ...) __WI_FOR_impN(24, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp26(fn, arg, ...) __WI_FOR_impN(25, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp27(fn, arg, ...) __WI_FOR_impN(26, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp28(fn, arg, ...) __WI_FOR_impN(27, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp29(fn, arg, ...) __WI_FOR_impN(28, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp30(fn, arg, ...) __WI_FOR_impN(29, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp31(fn, arg, ...) __WI_FOR_impN(30, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp32(fn, arg, ...) __WI_FOR_impN(31, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp33(fn, arg, ...) __WI_FOR_impN(32, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp34(fn, arg, ...) __WI_FOR_impN(33, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp35(fn, arg, ...) __WI_FOR_impN(34, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp36(fn, arg, ...) __WI_FOR_impN(35, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp37(fn, arg, ...) __WI_FOR_impN(36, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp38(fn, arg, ...) __WI_FOR_impN(37, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp39(fn, arg, ...) __WI_FOR_impN(38, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp40(fn, arg, ...) __WI_FOR_impN(39, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp41(fn, arg, ...) __WI_FOR_impN(40, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp42(fn, arg, ...) __WI_FOR_impN(41, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp43(fn, arg, ...) __WI_FOR_impN(42, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp44(fn, arg, ...) __WI_FOR_impN(43, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp45(fn, arg, ...) __WI_FOR_impN(44, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp46(fn, arg, ...) __WI_FOR_impN(45, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp47(fn, arg, ...) __WI_FOR_impN(46, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp48(fn, arg, ...) __WI_FOR_impN(47, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp49(fn, arg, ...) __WI_FOR_impN(48, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp50(fn, arg, ...) __WI_FOR_impN(49, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp51(fn, arg, ...) __WI_FOR_impN(50, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp52(fn, arg, ...) __WI_FOR_impN(51, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp53(fn, arg, ...) __WI_FOR_impN(52, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp54(fn, arg, ...) __WI_FOR_impN(53, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp55(fn, arg, ...) __WI_FOR_impN(54, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp56(fn, arg, ...) __WI_FOR_impN(55, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp57(fn, arg, ...) __WI_FOR_impN(56, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp58(fn, arg, ...) __WI_FOR_impN(57, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp59(fn, arg, ...) __WI_FOR_impN(58, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp60(fn, arg, ...) __WI_FOR_impN(59, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp61(fn, arg, ...) __WI_FOR_impN(60, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp62(fn, arg, ...) __WI_FOR_impN(61, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp63(fn, arg, ...) __WI_FOR_impN(62, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp64(fn, arg, ...) __WI_FOR_impN(63, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp65(fn, arg, ...) __WI_FOR_impN(64, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp66(fn, arg, ...) __WI_FOR_impN(65, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp67(fn, arg, ...) __WI_FOR_impN(66, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp68(fn, arg, ...) __WI_FOR_impN(67, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp69(fn, arg, ...) __WI_FOR_impN(68, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp70(fn, arg, ...) __WI_FOR_impN(69, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp71(fn, arg, ...) __WI_FOR_impN(70, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp72(fn, arg, ...) __WI_FOR_impN(71, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp73(fn, arg, ...) __WI_FOR_impN(72, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp74(fn, arg, ...) __WI_FOR_impN(73, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp75(fn, arg, ...) __WI_FOR_impN(74, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp76(fn, arg, ...) __WI_FOR_impN(75, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp77(fn, arg, ...) __WI_FOR_impN(76, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp78(fn, arg, ...) __WI_FOR_impN(77, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp79(fn, arg, ...) __WI_FOR_impN(78, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp80(fn, arg, ...) __WI_FOR_impN(79, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp81(fn, arg, ...) __WI_FOR_impN(80, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp82(fn, arg, ...) __WI_FOR_impN(81, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp83(fn, arg, ...) __WI_FOR_impN(82, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp84(fn, arg, ...) __WI_FOR_impN(83, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp85(fn, arg, ...) __WI_FOR_impN(84, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp86(fn, arg, ...) __WI_FOR_impN(85, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp87(fn, arg, ...) __WI_FOR_impN(86, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp88(fn, arg, ...) __WI_FOR_impN(87, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp89(fn, arg, ...) __WI_FOR_impN(88, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp90(fn, arg, ...) __WI_FOR_impN(89, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp91(fn, arg, ...) __WI_FOR_impN(90, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp92(fn, arg, ...) __WI_FOR_impN(91, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp93(fn, arg, ...) __WI_FOR_impN(92, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp94(fn, arg, ...) __WI_FOR_impN(93, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp95(fn, arg, ...) __WI_FOR_impN(94, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp96(fn, arg, ...) __WI_FOR_impN(95, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp97(fn, arg, ...) __WI_FOR_impN(96, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp98(fn, arg, ...) __WI_FOR_impN(97, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp99(fn, arg, ...) __WI_FOR_impN(98, fn, arg, fn, ##__VA_ARGS__)
|
||||
#define __WI_FOR_imp1( fn, arg, ...) __WI_FOR_impN( 0, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp2( fn, arg, ...) __WI_FOR_impN( 1, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp3( fn, arg, ...) __WI_FOR_impN( 2, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp4( fn, arg, ...) __WI_FOR_impN( 3, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp5( fn, arg, ...) __WI_FOR_impN( 4, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp6( fn, arg, ...) __WI_FOR_impN( 5, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp7( fn, arg, ...) __WI_FOR_impN( 6, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp8( fn, arg, ...) __WI_FOR_impN( 7, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp9( fn, arg, ...) __WI_FOR_impN( 8, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp10(fn, arg, ...) __WI_FOR_impN( 9, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp11(fn, arg, ...) __WI_FOR_impN(10, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp12(fn, arg, ...) __WI_FOR_impN(11, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp13(fn, arg, ...) __WI_FOR_impN(12, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp14(fn, arg, ...) __WI_FOR_impN(13, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp15(fn, arg, ...) __WI_FOR_impN(14, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp16(fn, arg, ...) __WI_FOR_impN(15, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp17(fn, arg, ...) __WI_FOR_impN(16, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp18(fn, arg, ...) __WI_FOR_impN(17, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp19(fn, arg, ...) __WI_FOR_impN(18, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp20(fn, arg, ...) __WI_FOR_impN(19, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp21(fn, arg, ...) __WI_FOR_impN(20, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp22(fn, arg, ...) __WI_FOR_impN(21, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp23(fn, arg, ...) __WI_FOR_impN(22, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp24(fn, arg, ...) __WI_FOR_impN(23, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp25(fn, arg, ...) __WI_FOR_impN(24, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp26(fn, arg, ...) __WI_FOR_impN(25, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp27(fn, arg, ...) __WI_FOR_impN(26, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp28(fn, arg, ...) __WI_FOR_impN(27, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp29(fn, arg, ...) __WI_FOR_impN(28, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp30(fn, arg, ...) __WI_FOR_impN(29, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp31(fn, arg, ...) __WI_FOR_impN(30, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp32(fn, arg, ...) __WI_FOR_impN(31, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp33(fn, arg, ...) __WI_FOR_impN(32, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp34(fn, arg, ...) __WI_FOR_impN(33, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp35(fn, arg, ...) __WI_FOR_impN(34, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp36(fn, arg, ...) __WI_FOR_impN(35, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp37(fn, arg, ...) __WI_FOR_impN(36, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp38(fn, arg, ...) __WI_FOR_impN(37, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp39(fn, arg, ...) __WI_FOR_impN(38, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp40(fn, arg, ...) __WI_FOR_impN(39, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp41(fn, arg, ...) __WI_FOR_impN(40, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp42(fn, arg, ...) __WI_FOR_impN(41, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp43(fn, arg, ...) __WI_FOR_impN(42, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp44(fn, arg, ...) __WI_FOR_impN(43, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp45(fn, arg, ...) __WI_FOR_impN(44, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp46(fn, arg, ...) __WI_FOR_impN(45, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp47(fn, arg, ...) __WI_FOR_impN(46, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp48(fn, arg, ...) __WI_FOR_impN(47, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp49(fn, arg, ...) __WI_FOR_impN(48, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp50(fn, arg, ...) __WI_FOR_impN(49, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp51(fn, arg, ...) __WI_FOR_impN(50, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp52(fn, arg, ...) __WI_FOR_impN(51, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp53(fn, arg, ...) __WI_FOR_impN(52, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp54(fn, arg, ...) __WI_FOR_impN(53, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp55(fn, arg, ...) __WI_FOR_impN(54, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp56(fn, arg, ...) __WI_FOR_impN(55, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp57(fn, arg, ...) __WI_FOR_impN(56, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp58(fn, arg, ...) __WI_FOR_impN(57, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp59(fn, arg, ...) __WI_FOR_impN(58, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp60(fn, arg, ...) __WI_FOR_impN(59, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp61(fn, arg, ...) __WI_FOR_impN(60, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp62(fn, arg, ...) __WI_FOR_impN(61, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp63(fn, arg, ...) __WI_FOR_impN(62, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp64(fn, arg, ...) __WI_FOR_impN(63, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp65(fn, arg, ...) __WI_FOR_impN(64, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp66(fn, arg, ...) __WI_FOR_impN(65, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp67(fn, arg, ...) __WI_FOR_impN(66, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp68(fn, arg, ...) __WI_FOR_impN(67, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp69(fn, arg, ...) __WI_FOR_impN(68, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp70(fn, arg, ...) __WI_FOR_impN(69, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp71(fn, arg, ...) __WI_FOR_impN(70, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp72(fn, arg, ...) __WI_FOR_impN(71, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp73(fn, arg, ...) __WI_FOR_impN(72, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp74(fn, arg, ...) __WI_FOR_impN(73, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp75(fn, arg, ...) __WI_FOR_impN(74, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp76(fn, arg, ...) __WI_FOR_impN(75, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp77(fn, arg, ...) __WI_FOR_impN(76, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp78(fn, arg, ...) __WI_FOR_impN(77, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp79(fn, arg, ...) __WI_FOR_impN(78, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp80(fn, arg, ...) __WI_FOR_impN(79, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp81(fn, arg, ...) __WI_FOR_impN(80, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp82(fn, arg, ...) __WI_FOR_impN(81, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp83(fn, arg, ...) __WI_FOR_impN(82, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp84(fn, arg, ...) __WI_FOR_impN(83, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp85(fn, arg, ...) __WI_FOR_impN(84, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp86(fn, arg, ...) __WI_FOR_impN(85, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp87(fn, arg, ...) __WI_FOR_impN(86, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp88(fn, arg, ...) __WI_FOR_impN(87, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp89(fn, arg, ...) __WI_FOR_impN(88, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp90(fn, arg, ...) __WI_FOR_impN(89, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp91(fn, arg, ...) __WI_FOR_impN(90, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp92(fn, arg, ...) __WI_FOR_impN(91, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp93(fn, arg, ...) __WI_FOR_impN(92, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp94(fn, arg, ...) __WI_FOR_impN(93, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp95(fn, arg, ...) __WI_FOR_impN(94, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp96(fn, arg, ...) __WI_FOR_impN(95, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp97(fn, arg, ...) __WI_FOR_impN(96, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp98(fn, arg, ...) __WI_FOR_impN(97, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_imp99(fn, arg, ...) __WI_FOR_impN(98, fn, arg, fn, __VA_ARGS__)
|
||||
#define __WI_FOR_impN(n, fn, arg, ...) \
|
||||
fn(arg) \
|
||||
WI_MACRO_INVOKE(WI_PASTE(__WI_FOR_imp, n), WI_FLATTEN((__VA_ARGS__)))
|
||||
WI_PASTE(__WI_FOR_imp, n) WI_FLATTEN((__VA_ARGS__))
|
||||
#define __WI_FOR_imp(n, fnAndArgs) WI_PASTE(__WI_FOR_imp, n) fnAndArgs
|
||||
/// @endcond
|
||||
|
||||
|
@ -372,14 +362,14 @@ Three exception modes are available:
|
|||
//! Set a single compile-time constant `flag` in the variable `var`.
|
||||
#define WI_SetFlag(var, flag) WI_SetAllFlags(var, WI_StaticAssertSingleBitSet(flag))
|
||||
//! Conditionally sets a single compile-time constant `flag` in the variable `var` only if `condition` is true.
|
||||
#define WI_SetFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_SetFlag(var, flag); } } while (0, 0)
|
||||
#define WI_SetFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_SetFlag(var, flag); } } while ((void)0, 0)
|
||||
|
||||
//! Clear zero or more bitflags specified by `flags` from the variable `var`.
|
||||
#define WI_ClearAllFlags(var, flags) ((var) &= ~(flags))
|
||||
//! Clear a single compile-time constant `flag` from the variable `var`.
|
||||
#define WI_ClearFlag(var, flag) WI_ClearAllFlags(var, WI_StaticAssertSingleBitSet(flag))
|
||||
//! Conditionally clear a single compile-time constant `flag` in the variable `var` only if `condition` is true.
|
||||
#define WI_ClearFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_ClearFlag(var, flag); } } while (0, 0)
|
||||
#define WI_ClearFlagIf(var, flag, condition) do { if (wil::verify_bool(condition)) { WI_ClearFlag(var, flag); } } while ((void)0, 0)
|
||||
|
||||
//! Changes a single compile-time constant `flag` in the variable `var` to be set if `isFlagSet` is true or cleared if `isFlagSet` is false.
|
||||
#define WI_UpdateFlag(var, flag, isFlagSet) (wil::verify_bool(isFlagSet) ? WI_SetFlag(var, flag) : WI_ClearFlag(var, flag))
|
||||
|
@ -435,7 +425,6 @@ WI_HEADER_INITITALIZATION_FUNCTION(InitializeDesktopFamilyApis, []
|
|||
{
|
||||
g_pfnGetModuleName = GetCurrentModuleName;
|
||||
g_pfnFailFastInLoaderCallout = FailFastInLoaderCallout;
|
||||
g_pfnRtlNtStatusToDosErrorNoTeb = RtlNtStatusToDosErrorNoTeb;
|
||||
return 1;
|
||||
});
|
||||
#endif
|
||||
|
@ -457,7 +446,7 @@ doing it with global function pointers and header initialization allows a runtim
|
|||
#endif
|
||||
|
||||
|
||||
/** All Windows Internal Library classes and functions are located within the "wil" namespace.
|
||||
/** All Windows Implementation Library classes and functions are located within the "wil" namespace.
|
||||
The 'wil' namespace is an intentionally short name as the intent is for code to be able to reference
|
||||
the namespace directly (example: `wil::srwlock lock;`) without a using statement. Resist adding a using
|
||||
statement for wil to avoid introducing potential name collisions between wil and other namespaces. */
|
||||
|
@ -578,7 +567,7 @@ namespace wil
|
|||
}
|
||||
|
||||
template <typename T, __R_ENABLE_IF_IS_NOT_CLASS(T)>
|
||||
__forceinline bool verify_bool(T val)
|
||||
__forceinline bool verify_bool(T /*val*/)
|
||||
{
|
||||
static_assert(!wistd::is_same<T, T>::value, "Wrong Type: bool/BOOL/BOOLEAN/boolean expected");
|
||||
}
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
|
||||
//*********************************************************
|
||||
//
|
||||
// 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
|
||||
|
||||
|
@ -28,7 +37,7 @@
|
|||
#define WINRT_EXTERNAL_CATCH_CLAUSE \
|
||||
catch (const wil::ResultException& e) \
|
||||
{ \
|
||||
return hresult_error(e.GetErrorCode(), to_hstring(e.what())).to_abi(); \
|
||||
return winrt::hresult_error(e.GetErrorCode(), winrt::to_hstring(e.what())).to_abi(); \
|
||||
}
|
||||
|
||||
namespace wil::details
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#ifndef __WIL_FILESYSTEM_INCLUDED
|
||||
#define __WIL_FILESYSTEM_INCLUDED
|
||||
|
||||
|
@ -477,7 +486,7 @@ namespace wil
|
|||
struct folder_change_reader_state
|
||||
{
|
||||
folder_change_reader_state(bool isRecursive, FolderChangeEvents filter, wistd::function<void(FolderChangeEvent, PCWSTR)> &&callback)
|
||||
: m_isRecursive(isRecursive), m_filter(filter), m_callback(wistd::move(callback))
|
||||
: m_callback(wistd::move(callback)), m_isRecursive(isRecursive), m_filter(filter)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -525,7 +534,7 @@ namespace wil
|
|||
{
|
||||
// This operation does not have the usual semantic of returning
|
||||
// ERROR_IO_PENDING.
|
||||
// NT_ASSERT(hr != HRESULT_FROM_WIN32(ERROR_IO_PENDING));
|
||||
// WI_ASSERT(hr != HRESULT_FROM_WIN32(ERROR_IO_PENDING));
|
||||
|
||||
// If the operation failed for whatever reason, ensure the TP
|
||||
// ref counts are accurate.
|
||||
|
@ -585,7 +594,7 @@ namespace wil
|
|||
ULONG result, ULONG_PTR /* BytesTransferred */, TP_IO * /* Io */)
|
||||
{
|
||||
auto readerState = static_cast<details::folder_change_reader_state *>(context);
|
||||
// NT_ASSERT(overlapped == &readerState->m_overlapped);
|
||||
// WI_ASSERT(overlapped == &readerState->m_overlapped);
|
||||
|
||||
bool requeue = true;
|
||||
if (result == ERROR_SUCCESS)
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
|
||||
//*********************************************************
|
||||
//
|
||||
// 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
|
||||
|
||||
|
@ -54,7 +63,7 @@ namespace wil
|
|||
struct registry_watcher_state
|
||||
{
|
||||
registry_watcher_state(unique_hkey &&keyToWatch, bool isRecursive, wistd::function<void(RegistryChangeKind)> &&callback)
|
||||
: m_keyToWatch(wistd::move(keyToWatch)), m_callback(wistd::move(callback)), m_isRecursive(isRecursive)
|
||||
: m_callback(wistd::move(callback)), m_keyToWatch(wistd::move(keyToWatch)), m_isRecursive(isRecursive)
|
||||
{
|
||||
}
|
||||
wistd::function<void(RegistryChangeKind)> m_callback;
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
|
||||
//*********************************************************
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
//*********************************************************
|
||||
#include "result_macros.h"
|
||||
#include "wistd_functional.h"
|
||||
#include "wistd_memory.h"
|
||||
|
@ -461,7 +470,7 @@ namespace wil
|
|||
}
|
||||
|
||||
lambda_call_log(lambda_call_log&& other) WI_NOEXCEPT :
|
||||
m_info(other.m_info), m_address(other.m_address), m_lambda(wistd::move(other.m_lambda)), m_call(other.m_call)
|
||||
m_address(other.m_address), m_info(other.m_info), m_lambda(wistd::move(other.m_lambda)), m_call(other.m_call)
|
||||
{
|
||||
other.m_call = false;
|
||||
}
|
||||
|
@ -1783,7 +1792,7 @@ namespace std
|
|||
{
|
||||
size_t operator()(wil::unique_any_t<storage_t> const &val) const
|
||||
{
|
||||
return (hash<wil::unique_any_t<storage_t>::pointer>()(val.get()));
|
||||
return (hash<typename wil::unique_any_t<storage_t>::pointer>()(val.get()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -1884,7 +1893,7 @@ namespace wil {
|
|||
|
||||
void reset(wistd::nullptr_t) WI_NOEXCEPT
|
||||
{
|
||||
static_assert(wistd::is_same<policy::pointer_invalid, wistd::nullptr_t>::value, "reset(nullptr): valid only for handle types using nullptr as the invalid value");
|
||||
static_assert(wistd::is_same<typename policy::pointer_invalid, wistd::nullptr_t>::value, "reset(nullptr): valid only for handle types using nullptr as the invalid value");
|
||||
reset();
|
||||
}
|
||||
|
||||
|
@ -1909,14 +1918,9 @@ namespace wil {
|
|||
return m_ptr.use_count();
|
||||
}
|
||||
|
||||
bool unique() const WI_NOEXCEPT
|
||||
{
|
||||
return m_ptr.unique();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename storage_t>
|
||||
friend class weak_any;
|
||||
friend class ::wil::weak_any;
|
||||
|
||||
std::shared_ptr<unique_t> m_ptr;
|
||||
};
|
||||
|
@ -1945,7 +1949,7 @@ namespace wil {
|
|||
|
||||
shared_any_t(wistd::nullptr_t) WI_NOEXCEPT
|
||||
{
|
||||
static_assert(wistd::is_same<policy::pointer_invalid, wistd::nullptr_t>::value, "nullptr constructor: valid only for handle types using nullptr as the invalid value");
|
||||
static_assert(wistd::is_same<typename policy::pointer_invalid, wistd::nullptr_t>::value, "nullptr constructor: valid only for handle types using nullptr as the invalid value");
|
||||
}
|
||||
|
||||
shared_any_t(shared_any_t &&other) WI_NOEXCEPT :
|
||||
|
@ -1986,7 +1990,7 @@ namespace wil {
|
|||
|
||||
shared_any_t& operator=(wistd::nullptr_t) WI_NOEXCEPT
|
||||
{
|
||||
static_assert(wistd::is_same<policy::pointer_invalid, wistd::nullptr_t>::value, "nullptr assignment: valid only for handle types using nullptr as the invalid value");
|
||||
static_assert(wistd::is_same<typename policy::pointer_invalid, wistd::nullptr_t>::value, "nullptr assignment: valid only for handle types using nullptr as the invalid value");
|
||||
storage_t::reset();
|
||||
return (*this);
|
||||
}
|
||||
|
@ -2005,14 +2009,14 @@ namespace wil {
|
|||
|
||||
pointer_storage *operator&()
|
||||
{
|
||||
static_assert(wistd::is_same<policy::pointer_access, details::pointer_access_all>::value, "operator & is not available for this handle");
|
||||
static_assert(wistd::is_same<typename policy::pointer_access, details::pointer_access_all>::value, "operator & is not available for this handle");
|
||||
storage_t::reset();
|
||||
return storage_t::addressof();
|
||||
}
|
||||
|
||||
pointer get() const WI_NOEXCEPT
|
||||
{
|
||||
static_assert(!wistd::is_same<policy::pointer_access, details::pointer_access_none>::value, "get(): the raw handle value is not available for this resource class");
|
||||
static_assert(!wistd::is_same<typename policy::pointer_access, details::pointer_access_none>::value, "get(): the raw handle value is not available for this resource class");
|
||||
return storage_t::get();
|
||||
}
|
||||
|
||||
|
@ -2038,14 +2042,14 @@ namespace wil {
|
|||
template <typename unique_t>
|
||||
bool operator==(const shared_any_t<unique_t>& left, wistd::nullptr_t) WI_NOEXCEPT
|
||||
{
|
||||
static_assert(wistd::is_same<shared_any_t<unique_t>::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value");
|
||||
static_assert(wistd::is_same<typename shared_any_t<unique_t>::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value");
|
||||
return !left;
|
||||
}
|
||||
|
||||
template <typename unique_t>
|
||||
bool operator==(wistd::nullptr_t, const shared_any_t<unique_t>& right) WI_NOEXCEPT
|
||||
{
|
||||
static_assert(wistd::is_same<shared_any_t<unique_t>::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value");
|
||||
static_assert(wistd::is_same<typename shared_any_t<unique_t>::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value");
|
||||
return !right;
|
||||
}
|
||||
|
||||
|
@ -2058,14 +2062,14 @@ namespace wil {
|
|||
template <typename unique_t>
|
||||
bool operator!=(const shared_any_t<unique_t>& left, wistd::nullptr_t) WI_NOEXCEPT
|
||||
{
|
||||
static_assert(wistd::is_same<shared_any_t<unique_t>::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value");
|
||||
static_assert(wistd::is_same<typename shared_any_t<unique_t>::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value");
|
||||
return !!left;
|
||||
}
|
||||
|
||||
template <typename unique_t>
|
||||
bool operator!=(wistd::nullptr_t, const shared_any_t<unique_t>& right) WI_NOEXCEPT
|
||||
{
|
||||
static_assert(wistd::is_same<shared_any_t<unique_t>::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value");
|
||||
static_assert(wistd::is_same<typename shared_any_t<unique_t>::policy::pointer_invalid, wistd::nullptr_t>::value, "the resource class does not use nullptr as an invalid value");
|
||||
return !!right;
|
||||
}
|
||||
|
||||
|
@ -2108,7 +2112,7 @@ namespace wil {
|
|||
}
|
||||
|
||||
weak_any(const shared_t &other) WI_NOEXCEPT :
|
||||
m_weakPtr(other.m_ptr)
|
||||
m_weakPtr(other.m_ptr)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -2175,7 +2179,7 @@ namespace std
|
|||
{
|
||||
size_t operator()(wil::shared_any_t<storage_t> const &val) const
|
||||
{
|
||||
return (hash<wil::shared_any_t<storage_t>::pointer>()(val.get()));
|
||||
return (hash<typename wil::shared_any_t<storage_t>::pointer>()(val.get()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -2660,11 +2664,9 @@ namespace wil
|
|||
class slim_event_t
|
||||
{
|
||||
public:
|
||||
slim_event_t()
|
||||
{
|
||||
}
|
||||
slim_event_t() WI_NOEXCEPT = default;
|
||||
|
||||
slim_event_t(bool isSignaled) :
|
||||
slim_event_t(bool isSignaled) WI_NOEXCEPT :
|
||||
m_isSignaled(isSignaled ? TRUE : FALSE)
|
||||
{
|
||||
}
|
||||
|
@ -3465,7 +3467,6 @@ namespace wil
|
|||
unique_event_nothrow m_event;
|
||||
// The thread pool must be last to ensure that the other members are valid
|
||||
// when it is destructed as it will reference them.
|
||||
// See http://osgvsowi/2224623
|
||||
unique_threadpool_wait m_threadPoolWait;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
|
||||
//*********************************************************
|
||||
//
|
||||
// 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_RESULT_INCLUDED
|
||||
#define __WIL_RESULT_INCLUDED
|
||||
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
|
||||
//*********************************************************
|
||||
//
|
||||
// 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_RESULTMACROS_INCLUDED
|
||||
#define __WIL_RESULTMACROS_INCLUDED
|
||||
|
||||
|
@ -91,6 +100,15 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "1")
|
|||
#else
|
||||
WI_ODR_PRAGMA("WIL_FreeMemory", "0")
|
||||
#endif
|
||||
|
||||
// It would appear as though the C++17 "noexcept is part of the type system" update in MSVC has "infected" the behavior
|
||||
// when compiling with C++14 (the default...), however the updated behavior for decltype understanding noexcept is _not_
|
||||
// present... So, work around it
|
||||
#if __WI_LIBCPP_STD_VER >= 17
|
||||
#define WI_PFN_NOEXCEPT WI_NOEXCEPT
|
||||
#else
|
||||
#define WI_PFN_NOEXCEPT
|
||||
#endif
|
||||
/// @endcond
|
||||
|
||||
#if defined(__cplusplus) && !defined(__WIL_MIN_KERNEL) && !defined(WIL_KERNEL_MODE)
|
||||
|
@ -544,23 +562,23 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "0")
|
|||
#endif
|
||||
// end-of-repeated fail-fast handling macros
|
||||
// Helpers for return macros
|
||||
#define __RETURN_HR_MSG(hr, str, fmt, ...) do { HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_HrMsg)(__R_INFO(str) __hr, fmt, __VA_ARGS__); } return __hr; } while (0, 0)
|
||||
#define __RETURN_HR_MSG_FAIL(hr, str, fmt, ...) do { HRESULT __hr = (hr); __R_FN(Return_HrMsg)(__R_INFO(str) __hr, fmt, __VA_ARGS__); return __hr; } while (0, 0)
|
||||
#define __RETURN_WIN32_MSG(err, str, fmt, ...) do { DWORD __err = (err); if (FAILED_WIN32(__err)) { return __R_FN(Return_Win32Msg)(__R_INFO(str) __err, fmt, __VA_ARGS__); } return S_OK; } while (0, 0)
|
||||
#define __RETURN_WIN32_MSG_FAIL(err, str, fmt, ...) do { DWORD __err = (err); return __R_FN(Return_Win32Msg)(__R_INFO(str) __err, fmt, __VA_ARGS__); } while (0, 0)
|
||||
#define __RETURN_HR_MSG(hr, str, fmt, ...) do { HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_HrMsg)(__R_INFO(str) __hr, fmt, __VA_ARGS__); } return __hr; } while ((void)0, 0)
|
||||
#define __RETURN_HR_MSG_FAIL(hr, str, fmt, ...) do { HRESULT __hr = (hr); __R_FN(Return_HrMsg)(__R_INFO(str) __hr, fmt, __VA_ARGS__); return __hr; } while ((void)0, 0)
|
||||
#define __RETURN_WIN32_MSG(err, str, fmt, ...) do { DWORD __err = (err); if (FAILED_WIN32(__err)) { return __R_FN(Return_Win32Msg)(__R_INFO(str) __err, fmt, __VA_ARGS__); } return S_OK; } while ((void)0, 0)
|
||||
#define __RETURN_WIN32_MSG_FAIL(err, str, fmt, ...) do { DWORD __err = (err); return __R_FN(Return_Win32Msg)(__R_INFO(str) __err, fmt, __VA_ARGS__); } while ((void)0, 0)
|
||||
#define __RETURN_GLE_MSG_FAIL(str, fmt, ...) return __R_FN(Return_GetLastErrorMsg)(__R_INFO(str) fmt, __VA_ARGS__)
|
||||
#define __RETURN_NTSTATUS_MSG(status, str, fmt, ...) do { NTSTATUS __status = (status); if(FAILED_NTSTATUS(__status)) { return __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, __VA_ARGS__); } return S_OK; } while (0, 0)
|
||||
#define __RETURN_NTSTATUS_MSG_FAIL(status, str, fmt, ...) do { NTSTATUS __status = (status); return __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, __VA_ARGS__); } while (0, 0)
|
||||
#define __RETURN_HR(hr, str) do { HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_Hr)(__R_INFO(str) __hr); } return __hr; } while (0, 0)
|
||||
#define __RETURN_HR_NOFILE(hr, str) do { HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_Hr)(__R_INFO_NOFILE(str) __hr); } return __hr; } while (0, 0)
|
||||
#define __RETURN_HR_FAIL(hr, str) do { HRESULT __hr = (hr); __R_FN(Return_Hr)(__R_INFO(str) __hr); return __hr; } while (0, 0)
|
||||
#define __RETURN_HR_FAIL_NOFILE(hr, str) do { HRESULT __hr = (hr); __R_FN(Return_Hr)(__R_INFO_NOFILE(str) __hr); return __hr; } while (0, 0)
|
||||
#define __RETURN_WIN32(err, str) do { DWORD __err = (err); if (FAILED_WIN32(__err)) { return __R_FN(Return_Win32)(__R_INFO(str) __err); } return S_OK; } while (0, 0)
|
||||
#define __RETURN_WIN32_FAIL(err, str) do { DWORD __err = (err); return __R_FN(Return_Win32)(__R_INFO(str) __err); } while (0, 0)
|
||||
#define __RETURN_NTSTATUS_MSG(status, str, fmt, ...) do { NTSTATUS __status = (status); if(FAILED_NTSTATUS(__status)) { return __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, __VA_ARGS__); } return S_OK; } while ((void)0, 0)
|
||||
#define __RETURN_NTSTATUS_MSG_FAIL(status, str, fmt, ...) do { NTSTATUS __status = (status); return __R_FN(Return_NtStatusMsg)(__R_INFO(str) __status, fmt, __VA_ARGS__); } while ((void)0, 0)
|
||||
#define __RETURN_HR(hr, str) do { HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_Hr)(__R_INFO(str) __hr); } return __hr; } while ((void)0, 0)
|
||||
#define __RETURN_HR_NOFILE(hr, str) do { HRESULT __hr = (hr); if (FAILED(__hr)) { __R_FN(Return_Hr)(__R_INFO_NOFILE(str) __hr); } return __hr; } while ((void)0, 0)
|
||||
#define __RETURN_HR_FAIL(hr, str) do { HRESULT __hr = (hr); __R_FN(Return_Hr)(__R_INFO(str) __hr); return __hr; } while ((void)0, 0)
|
||||
#define __RETURN_HR_FAIL_NOFILE(hr, str) do { HRESULT __hr = (hr); __R_FN(Return_Hr)(__R_INFO_NOFILE(str) __hr); return __hr; } while ((void)0, 0)
|
||||
#define __RETURN_WIN32(err, str) do { DWORD __err = (err); if (FAILED_WIN32(__err)) { return __R_FN(Return_Win32)(__R_INFO(str) __err); } return S_OK; } while ((void)0, 0)
|
||||
#define __RETURN_WIN32_FAIL(err, str) do { DWORD __err = (err); return __R_FN(Return_Win32)(__R_INFO(str) __err); } while ((void)0, 0)
|
||||
#define __RETURN_GLE_FAIL(str) return __R_FN(Return_GetLastError)(__R_INFO_ONLY(str))
|
||||
#define __RETURN_GLE_FAIL_NOFILE(str) return __R_FN(Return_GetLastError)(__R_INFO_NOFILE_ONLY(str))
|
||||
#define __RETURN_NTSTATUS(status, str) do { NTSTATUS __status = (status); if(FAILED_NTSTATUS(__status)) { return __R_FN(Return_NtStatus)(__R_INFO(str) __status); } return S_OK; } while (0, 0)
|
||||
#define __RETURN_NTSTATUS_FAIL(status, str) do { NTSTATUS __status = (status); return __R_FN(Return_NtStatus)(__R_INFO(str) __status); } while (0, 0)
|
||||
#define __RETURN_NTSTATUS(status, str) do { NTSTATUS __status = (status); if(FAILED_NTSTATUS(__status)) { return __R_FN(Return_NtStatus)(__R_INFO(str) __status); } return S_OK; } while ((void)0, 0)
|
||||
#define __RETURN_NTSTATUS_FAIL(status, str) do { NTSTATUS __status = (status); return __R_FN(Return_NtStatus)(__R_INFO(str) __status); } while ((void)0, 0)
|
||||
/// @endcond
|
||||
|
||||
//*****************************************************************************
|
||||
|
@ -574,15 +592,15 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "0")
|
|||
#define RETURN_NTSTATUS(status) __RETURN_NTSTATUS(status, #status)
|
||||
|
||||
// Conditionally returns failures (HRESULT) - always logs failures
|
||||
#define RETURN_IF_FAILED(hr) do { HRESULT __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_FAIL(__hrRet, #hr); }} while (0, 0)
|
||||
#define RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) do { BOOL __boolRet = wil::verify_BOOL(win32BOOL); if (!__boolRet) { __RETURN_GLE_FAIL(#win32BOOL); }} while (0, 0)
|
||||
#define RETURN_IF_WIN32_ERROR(win32err) do { DWORD __errRet = (win32err); if (FAILED_WIN32(__errRet)) { __RETURN_WIN32_FAIL(__errRet, #win32err); }} while (0, 0)
|
||||
#define RETURN_IF_NULL_ALLOC(ptr) do { if ((ptr) == nullptr) { __RETURN_HR_FAIL(E_OUTOFMEMORY, #ptr); }} while (0, 0)
|
||||
#define RETURN_HR_IF(hr, condition) do { if (wil::verify_bool(condition)) { __RETURN_HR(wil::verify_hresult(hr), #condition); }} while (0, 0)
|
||||
#define RETURN_HR_IF_NULL(hr, ptr) do { if ((ptr) == nullptr) { __RETURN_HR(wil::verify_hresult(hr), #ptr); }} while (0, 0)
|
||||
#define RETURN_LAST_ERROR_IF(condition) do { if (wil::verify_bool(condition)) { __RETURN_GLE_FAIL(#condition); }} while (0, 0)
|
||||
#define RETURN_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { __RETURN_GLE_FAIL(#ptr); }} while (0, 0)
|
||||
#define RETURN_IF_NTSTATUS_FAILED(status) do { NTSTATUS __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { __RETURN_NTSTATUS_FAIL(__statusRet, #status); }} while (0, 0)
|
||||
#define RETURN_IF_FAILED(hr) do { HRESULT __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_FAIL(__hrRet, #hr); }} while ((void)0, 0)
|
||||
#define RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) do { BOOL __boolRet = wil::verify_BOOL(win32BOOL); if (!__boolRet) { __RETURN_GLE_FAIL(#win32BOOL); }} while ((void)0, 0)
|
||||
#define RETURN_IF_WIN32_ERROR(win32err) do { DWORD __errRet = (win32err); if (FAILED_WIN32(__errRet)) { __RETURN_WIN32_FAIL(__errRet, #win32err); }} while ((void)0, 0)
|
||||
#define RETURN_IF_NULL_ALLOC(ptr) do { if ((ptr) == nullptr) { __RETURN_HR_FAIL(E_OUTOFMEMORY, #ptr); }} while ((void)0, 0)
|
||||
#define RETURN_HR_IF(hr, condition) do { if (wil::verify_bool(condition)) { __RETURN_HR(wil::verify_hresult(hr), #condition); }} while ((void)0, 0)
|
||||
#define RETURN_HR_IF_NULL(hr, ptr) do { if ((ptr) == nullptr) { __RETURN_HR(wil::verify_hresult(hr), #ptr); }} while ((void)0, 0)
|
||||
#define RETURN_LAST_ERROR_IF(condition) do { if (wil::verify_bool(condition)) { __RETURN_GLE_FAIL(#condition); }} while ((void)0, 0)
|
||||
#define RETURN_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { __RETURN_GLE_FAIL(#ptr); }} while ((void)0, 0)
|
||||
#define RETURN_IF_NTSTATUS_FAILED(status) do { NTSTATUS __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { __RETURN_NTSTATUS_FAIL(__statusRet, #status); }} while ((void)0, 0)
|
||||
|
||||
// Always returns a known failure (HRESULT) - always logs a var-arg message on failure
|
||||
#define RETURN_HR_MSG(hr, fmt, ...) __RETURN_HR_MSG(wil::verify_hresult(hr), #hr, fmt, __VA_ARGS__)
|
||||
|
@ -591,26 +609,26 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "0")
|
|||
#define RETURN_NTSTATUS_MSG(status, fmt, ...) __RETURN_NTSTATUS_MSG(status, #status, fmt, __VA_ARGS__)
|
||||
|
||||
// Conditionally returns failures (HRESULT) - always logs a var-arg message on failure
|
||||
#define RETURN_IF_FAILED_MSG(hr, fmt, ...) do { auto __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_MSG_FAIL(__hrRet, #hr, fmt, __VA_ARGS__); }} while (0, 0)
|
||||
#define RETURN_IF_WIN32_BOOL_FALSE_MSG(win32BOOL, fmt, ...) do { if (!wil::verify_BOOL(win32BOOL)) { __RETURN_GLE_MSG_FAIL(#win32BOOL, fmt, __VA_ARGS__); }} while (0, 0)
|
||||
#define RETURN_IF_WIN32_ERROR_MSG(win32err, fmt, ...) do { auto __errRet = (win32err); if (FAILED_WIN32(__errRet)) { __RETURN_WIN32_MSG_FAIL(__errRet, #win32err, fmt, __VA_ARGS__); }} while (0, 0)
|
||||
#define RETURN_IF_NULL_ALLOC_MSG(ptr, fmt, ...) do { if ((ptr) == nullptr) { __RETURN_HR_MSG_FAIL(E_OUTOFMEMORY, #ptr, fmt, __VA_ARGS__); }} while (0, 0)
|
||||
#define RETURN_HR_IF_MSG(hr, condition, fmt, ...) do { if (wil::verify_bool(condition)) { __RETURN_HR_MSG(wil::verify_hresult(hr), #condition, fmt, __VA_ARGS__); }} while (0, 0)
|
||||
#define RETURN_HR_IF_NULL_MSG(hr, ptr, fmt, ...) do { if ((ptr) == nullptr) { __RETURN_HR_MSG(wil::verify_hresult(hr), #ptr, fmt, __VA_ARGS__); }} while (0, 0)
|
||||
#define RETURN_LAST_ERROR_IF_MSG(condition, fmt, ...) do { if (wil::verify_bool(condition)) { __RETURN_GLE_MSG_FAIL(#condition, fmt, __VA_ARGS__); }} while (0, 0)
|
||||
#define RETURN_LAST_ERROR_IF_NULL_MSG(ptr, fmt, ...) do { if ((ptr) == nullptr) { __RETURN_GLE_MSG_FAIL(#ptr, fmt, __VA_ARGS__); }} while (0, 0)
|
||||
#define RETURN_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) do { NTSTATUS __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { __RETURN_NTSTATUS_MSG_FAIL(__statusRet, #status, fmt, __VA_ARGS__); }} while (0, 0)
|
||||
#define RETURN_IF_FAILED_MSG(hr, fmt, ...) do { auto __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_MSG_FAIL(__hrRet, #hr, fmt, __VA_ARGS__); }} while ((void)0, 0)
|
||||
#define RETURN_IF_WIN32_BOOL_FALSE_MSG(win32BOOL, fmt, ...) do { if (!wil::verify_BOOL(win32BOOL)) { __RETURN_GLE_MSG_FAIL(#win32BOOL, fmt, __VA_ARGS__); }} while ((void)0, 0)
|
||||
#define RETURN_IF_WIN32_ERROR_MSG(win32err, fmt, ...) do { auto __errRet = (win32err); if (FAILED_WIN32(__errRet)) { __RETURN_WIN32_MSG_FAIL(__errRet, #win32err, fmt, __VA_ARGS__); }} while ((void)0, 0)
|
||||
#define RETURN_IF_NULL_ALLOC_MSG(ptr, fmt, ...) do { if ((ptr) == nullptr) { __RETURN_HR_MSG_FAIL(E_OUTOFMEMORY, #ptr, fmt, __VA_ARGS__); }} while ((void)0, 0)
|
||||
#define RETURN_HR_IF_MSG(hr, condition, fmt, ...) do { if (wil::verify_bool(condition)) { __RETURN_HR_MSG(wil::verify_hresult(hr), #condition, fmt, __VA_ARGS__); }} while ((void)0, 0)
|
||||
#define RETURN_HR_IF_NULL_MSG(hr, ptr, fmt, ...) do { if ((ptr) == nullptr) { __RETURN_HR_MSG(wil::verify_hresult(hr), #ptr, fmt, __VA_ARGS__); }} while ((void)0, 0)
|
||||
#define RETURN_LAST_ERROR_IF_MSG(condition, fmt, ...) do { if (wil::verify_bool(condition)) { __RETURN_GLE_MSG_FAIL(#condition, fmt, __VA_ARGS__); }} while ((void)0, 0)
|
||||
#define RETURN_LAST_ERROR_IF_NULL_MSG(ptr, fmt, ...) do { if ((ptr) == nullptr) { __RETURN_GLE_MSG_FAIL(#ptr, fmt, __VA_ARGS__); }} while ((void)0, 0)
|
||||
#define RETURN_IF_NTSTATUS_FAILED_MSG(status, fmt, ...) do { NTSTATUS __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { __RETURN_NTSTATUS_MSG_FAIL(__statusRet, #status, fmt, __VA_ARGS__); }} while ((void)0, 0)
|
||||
|
||||
// Conditionally returns failures (HRESULT) - use for failures that are expected in common use - failures are not logged - macros are only for control flow pattern
|
||||
#define RETURN_IF_FAILED_EXPECTED(hr) do { auto __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { return __hrRet; }} while (0, 0)
|
||||
#define RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(win32BOOL) do { if (!wil::verify_BOOL(win32BOOL)) { return wil::details::GetLastErrorFailHr(); }} while (0, 0)
|
||||
#define RETURN_IF_WIN32_ERROR_EXPECTED(win32err) do { auto __errRet = (win32err); if (FAILED_WIN32(__errRet)) { return HRESULT_FROM_WIN32(__errRet); }} while (0, 0)
|
||||
#define RETURN_IF_NULL_ALLOC_EXPECTED(ptr) do { if ((ptr) == nullptr) { return E_OUTOFMEMORY; }} while (0, 0)
|
||||
#define RETURN_HR_IF_EXPECTED(hr, condition) do { if (wil::verify_bool(condition)) { return wil::verify_hresult(hr); }} while (0, 0)
|
||||
#define RETURN_HR_IF_NULL_EXPECTED(hr, ptr) do { if ((ptr) == nullptr) { return wil::verify_hresult(hr); }} while (0, 0)
|
||||
#define RETURN_LAST_ERROR_IF_EXPECTED(condition) do { if (wil::verify_bool(condition)) { return wil::details::GetLastErrorFailHr(); }} while (0, 0)
|
||||
#define RETURN_LAST_ERROR_IF_NULL_EXPECTED(ptr) do { if ((ptr) == nullptr) { return wil::details::GetLastErrorFailHr(); }} while (0, 0)
|
||||
#define RETURN_IF_NTSTATUS_FAILED_EXPECTED(status) do { auto __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { return wil::details::NtStatusToHr(__statusRet); }} while (0, 0)
|
||||
#define RETURN_IF_FAILED_EXPECTED(hr) do { auto __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { return __hrRet; }} while ((void)0, 0)
|
||||
#define RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(win32BOOL) do { if (!wil::verify_BOOL(win32BOOL)) { return wil::details::GetLastErrorFailHr(); }} while ((void)0, 0)
|
||||
#define RETURN_IF_WIN32_ERROR_EXPECTED(win32err) do { auto __errRet = (win32err); if (FAILED_WIN32(__errRet)) { return HRESULT_FROM_WIN32(__errRet); }} while ((void)0, 0)
|
||||
#define RETURN_IF_NULL_ALLOC_EXPECTED(ptr) do { if ((ptr) == nullptr) { return E_OUTOFMEMORY; }} while ((void)0, 0)
|
||||
#define RETURN_HR_IF_EXPECTED(hr, condition) do { if (wil::verify_bool(condition)) { return wil::verify_hresult(hr); }} while ((void)0, 0)
|
||||
#define RETURN_HR_IF_NULL_EXPECTED(hr, ptr) do { if ((ptr) == nullptr) { return wil::verify_hresult(hr); }} while ((void)0, 0)
|
||||
#define RETURN_LAST_ERROR_IF_EXPECTED(condition) do { if (wil::verify_bool(condition)) { return wil::details::GetLastErrorFailHr(); }} while ((void)0, 0)
|
||||
#define RETURN_LAST_ERROR_IF_NULL_EXPECTED(ptr) do { if ((ptr) == nullptr) { return wil::details::GetLastErrorFailHr(); }} while ((void)0, 0)
|
||||
#define RETURN_IF_NTSTATUS_FAILED_EXPECTED(status) do { auto __statusRet = (status); if (FAILED_NTSTATUS(__statusRet)) { return wil::details::NtStatusToHr(__statusRet); }} while ((void)0, 0)
|
||||
|
||||
#define __WI_OR_IS_EXPECTED_HRESULT(e) || (__hrRet == wil::verify_hresult(e))
|
||||
#define RETURN_IF_FAILED_WITH_EXPECTED(hr, hrExpected, ...) \
|
||||
|
@ -626,7 +644,7 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "0")
|
|||
__RETURN_HR_FAIL(__hrRet, #hr); \
|
||||
} \
|
||||
} \
|
||||
while (0, 0)
|
||||
while ((void)0, 0)
|
||||
|
||||
//*****************************************************************************
|
||||
// Macros for logging failures (ignore or pass-through)
|
||||
|
@ -744,7 +762,7 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "0")
|
|||
#define FAIL_FAST_IMMEDIATE_IF_NTSTATUS_FAILED(status) __RFF_FN(FailFastImmediate_IfNtStatusFailed)(status)
|
||||
|
||||
// Specializations
|
||||
#define FAIL_FAST_IMMEDIATE_IF_IN_LOADER_CALLOUT() do { if (wil::details::g_pfnFailFastInLoaderCallout != nullptr) { wil::details::g_pfnFailFastInLoaderCallout(); } } while (0, 0)
|
||||
#define FAIL_FAST_IMMEDIATE_IF_IN_LOADER_CALLOUT() do { if (wil::details::g_pfnFailFastInLoaderCallout != nullptr) { wil::details::g_pfnFailFastInLoaderCallout(); } } while ((void)0, 0)
|
||||
|
||||
|
||||
//*****************************************************************************
|
||||
|
@ -837,14 +855,14 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "0")
|
|||
#define WI_USAGE_ASSERT_STOP(condition) WI_ASSERT(condition)
|
||||
#endif
|
||||
#ifdef RESULT_DEBUG
|
||||
#define WI_USAGE_ERROR(msg, ...) do { LOG_HR_MSG(HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE), msg, __VA_ARGS__); WI_USAGE_ASSERT_STOP(false); } while (0, 0)
|
||||
#define WI_USAGE_ERROR_FORWARD(msg, ...) do { ReportFailure_ReplaceMsg(__R_FN_CALL_FULL, FailureType::Log, HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE), msg, __VA_ARGS__); WI_USAGE_ASSERT_STOP(false); } while (0, 0)
|
||||
#define WI_USAGE_ERROR(msg, ...) do { LOG_HR_MSG(HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE), msg, __VA_ARGS__); WI_USAGE_ASSERT_STOP(false); } while ((void)0, 0)
|
||||
#define WI_USAGE_ERROR_FORWARD(msg, ...) do { ReportFailure_ReplaceMsg(__R_FN_CALL_FULL, FailureType::Log, HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE), msg, __VA_ARGS__); WI_USAGE_ASSERT_STOP(false); } while ((void)0, 0)
|
||||
#else
|
||||
#define WI_USAGE_ERROR(msg, ...) do { LOG_HR(HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE)); WI_USAGE_ASSERT_STOP(false); } while (0, 0)
|
||||
#define WI_USAGE_ERROR_FORWARD(msg, ...) do { ReportFailure_Hr(__R_FN_CALL_FULL, FailureType::Log, HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE)); WI_USAGE_ASSERT_STOP(false); } while (0, 0)
|
||||
#define WI_USAGE_ERROR(msg, ...) do { LOG_HR(HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE)); WI_USAGE_ASSERT_STOP(false); } while ((void)0, 0)
|
||||
#define WI_USAGE_ERROR_FORWARD(msg, ...) do { ReportFailure_Hr(__R_FN_CALL_FULL, FailureType::Log, HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE)); WI_USAGE_ASSERT_STOP(false); } while ((void)0, 0)
|
||||
#endif
|
||||
#define WI_USAGE_VERIFY(condition, msg, ...) do { auto __passed = wil::verify_bool(condition); if (!__passed) { WI_USAGE_ERROR(msg, __VA_ARGS__); }} while (0, 0)
|
||||
#define WI_USAGE_VERIFY_FORWARD(condition, msg, ...) do { auto __passed = wil::verify_bool(condition); if (!__passed) { WI_USAGE_ERROR_FORWARD(msg, __VA_ARGS__); }} while (0, 0)
|
||||
#define WI_USAGE_VERIFY(condition, msg, ...) do { auto __passed = wil::verify_bool(condition); if (!__passed) { WI_USAGE_ERROR(msg, __VA_ARGS__); }} while ((void)0, 0)
|
||||
#define WI_USAGE_VERIFY_FORWARD(condition, msg, ...) do { auto __passed = wil::verify_bool(condition); if (!__passed) { WI_USAGE_ERROR_FORWARD(msg, __VA_ARGS__); }} while ((void)0, 0)
|
||||
#ifdef RESULT_DEBUG
|
||||
#define WI_USAGE_ASSERT(condition, msg, ...) WI_USAGE_VERIFY(condition, msg, __VA_ARGS__)
|
||||
#else
|
||||
|
@ -866,12 +884,12 @@ WI_ODR_PRAGMA("WIL_FreeMemory", "0")
|
|||
#define __WIL_PRIVATE_FAIL_FAST_HR(hr) FAIL_FAST_HR(hr)
|
||||
#define __WIL_PRIVATE_LOG_HR(hr) LOG_HR(hr)
|
||||
#else
|
||||
#define __WIL_PRIVATE_RETURN_IF_FAILED(hr) do { HRESULT __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_FAIL_NOFILE(__hrRet, #hr); }} while (0, 0)
|
||||
#define __WIL_PRIVATE_RETURN_HR_IF(hr, cond) do { if (wil::verify_bool(cond)) { __RETURN_HR_NOFILE(wil::verify_hresult(hr), #cond); }} while (0, 0)
|
||||
#define __WIL_PRIVATE_RETURN_LAST_ERROR_IF(cond) do { if (wil::verify_bool(cond)) { __RETURN_GLE_FAIL_NOFILE(#cond); }} while (0, 0)
|
||||
#define __WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) do { BOOL __boolRet = wil::verify_BOOL(win32BOOL); if (!__boolRet) { __RETURN_GLE_FAIL_NOFILE(#win32BOOL); }} while (0, 0)
|
||||
#define __WIL_PRIVATE_RETURN_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { __RETURN_GLE_FAIL_NOFILE(#ptr); }} while (0, 0)
|
||||
#define __WIL_PRIVATE_RETURN_IF_NULL_ALLOC(ptr) do { if ((ptr) == nullptr) { __RETURN_HR_FAIL_NOFILE(E_OUTOFMEMORY, #ptr); }} while (0, 0)
|
||||
#define __WIL_PRIVATE_RETURN_IF_FAILED(hr) do { HRESULT __hrRet = wil::verify_hresult(hr); if (FAILED(__hrRet)) { __RETURN_HR_FAIL_NOFILE(__hrRet, #hr); }} while ((void)0, 0)
|
||||
#define __WIL_PRIVATE_RETURN_HR_IF(hr, cond) do { if (wil::verify_bool(cond)) { __RETURN_HR_NOFILE(wil::verify_hresult(hr), #cond); }} while ((void)0, 0)
|
||||
#define __WIL_PRIVATE_RETURN_LAST_ERROR_IF(cond) do { if (wil::verify_bool(cond)) { __RETURN_GLE_FAIL_NOFILE(#cond); }} while ((void)0, 0)
|
||||
#define __WIL_PRIVATE_RETURN_IF_WIN32_BOOL_FALSE(win32BOOL) do { BOOL __boolRet = wil::verify_BOOL(win32BOOL); if (!__boolRet) { __RETURN_GLE_FAIL_NOFILE(#win32BOOL); }} while ((void)0, 0)
|
||||
#define __WIL_PRIVATE_RETURN_LAST_ERROR_IF_NULL(ptr) do { if ((ptr) == nullptr) { __RETURN_GLE_FAIL_NOFILE(#ptr); }} while ((void)0, 0)
|
||||
#define __WIL_PRIVATE_RETURN_IF_NULL_ALLOC(ptr) do { if ((ptr) == nullptr) { __RETURN_HR_FAIL_NOFILE(E_OUTOFMEMORY, #ptr); }} while ((void)0, 0)
|
||||
#define __WIL_PRIVATE_RETURN_LAST_ERROR() __RETURN_GLE_FAIL_NOFILE(nullptr)
|
||||
#define __WIL_PRIVATE_FAIL_FAST_HR_IF(hr, condition) __RFF_FN(FailFast_HrIf)(__RFF_INFO_NOFILE(#condition) wil::verify_hresult(hr), wil::verify_bool(condition))
|
||||
#define __WIL_PRIVATE_FAIL_FAST_HR(hr) __RFF_FN(FailFast_Hr)(__RFF_INFO_NOFILE(#hr) wil::verify_hresult(hr))
|
||||
|
@ -971,7 +989,7 @@ namespace wil
|
|||
// [optionally] Plug in error logging
|
||||
// Note: This callback is deprecated. Please use SetResultTelemetryFallback for telemetry or
|
||||
// SetResultLoggingCallback for observation.
|
||||
extern "C" __declspec(selectany) void(__stdcall *g_pfnResultLoggingCallback)(_Inout_ wil::FailureInfo *pFailure, _Inout_updates_opt_z_(cchDebugMessage) PWSTR pszDebugMessage, _Pre_satisfies_(cchDebugMessage > 0) size_t cchDebugMessage) WI_NOEXCEPT = nullptr;
|
||||
extern "C" __declspec(selectany) void(__stdcall *g_pfnResultLoggingCallback)(_Inout_ wil::FailureInfo *pFailure, _Inout_updates_opt_z_(cchDebugMessage) PWSTR pszDebugMessage, _Pre_satisfies_(cchDebugMessage > 0) size_t cchDebugMessage) WI_PFN_NOEXCEPT = nullptr;
|
||||
|
||||
// [optional]
|
||||
// This can be explicitly set to control whether or not error messages will be output to OutputDebugString. It can also
|
||||
|
@ -981,13 +999,13 @@ namespace wil
|
|||
// [optionally] Allows application to specify a debugger to detect whether a debugger is present.
|
||||
// Useful for processes that can only be debugged under kernel debuggers where IsDebuggerPresent returns
|
||||
// false.
|
||||
__declspec(selectany) bool(__stdcall *g_pfnIsDebuggerPresent)() WI_NOEXCEPT = nullptr;
|
||||
__declspec(selectany) bool(__stdcall *g_pfnIsDebuggerPresent)() WI_PFN_NOEXCEPT = nullptr;
|
||||
|
||||
// [optionally] Allows forcing WIL to believe a debugger is present. Useful for when a kernel debugger is attached and ::IsDebuggerPresent returns false
|
||||
__declspec(selectany) bool g_fIsDebuggerPresent = false;
|
||||
|
||||
// [optionally] Plug in additional exception-type support (return S_OK when *unable* to remap the exception)
|
||||
__declspec(selectany) HRESULT(__stdcall *g_pfnResultFromCaughtException)() WI_NOEXCEPT = nullptr;
|
||||
__declspec(selectany) HRESULT(__stdcall *g_pfnResultFromCaughtException)() WI_PFN_NOEXCEPT = nullptr;
|
||||
|
||||
// [optionally] Use to configure fast fail of unknown exceptions (turn them off).
|
||||
__declspec(selectany) bool g_fResultFailFastUnknownExceptions = true;
|
||||
|
@ -1002,7 +1020,7 @@ namespace wil
|
|||
__declspec(selectany) bool g_fBreakOnFailure = false;
|
||||
|
||||
// [optionally] customize failfast behavior
|
||||
__declspec(selectany) bool(__stdcall *g_pfnWilFailFast)(const wil::FailureInfo& info) WI_NOEXCEPT = nullptr;
|
||||
__declspec(selectany) bool(__stdcall *g_pfnWilFailFast)(const wil::FailureInfo& info) WI_PFN_NOEXCEPT = nullptr;
|
||||
|
||||
/// @cond
|
||||
namespace details
|
||||
|
@ -1139,31 +1157,34 @@ namespace wil
|
|||
};
|
||||
|
||||
// Fallback telemetry provider callback (set with wil::SetResultTelemetryFallback)
|
||||
__declspec(selectany) void(__stdcall *g_pfnTelemetryCallback)(bool alreadyReported, wil::FailureInfo const &failure) WI_NOEXCEPT = nullptr;
|
||||
__declspec(selectany) void(__stdcall *g_pfnTelemetryCallback)(bool alreadyReported, wil::FailureInfo const &failure) WI_PFN_NOEXCEPT = nullptr;
|
||||
|
||||
// Result.h plug-in (WIL use only)
|
||||
__declspec(selectany) void(__stdcall *g_pfnGetContextAndNotifyFailure)(_Inout_ FailureInfo *pFailure, _Out_writes_(callContextStringLength) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringLength > 0) size_t callContextStringLength) WI_NOEXCEPT = nullptr;
|
||||
__declspec(selectany) void(__stdcall *g_pfnGetContextAndNotifyFailure)(_Inout_ FailureInfo *pFailure, _Out_writes_(callContextStringLength) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringLength > 0) size_t callContextStringLength) WI_PFN_NOEXCEPT = nullptr;
|
||||
|
||||
// Observe all errors flowing through the system with this callback (set with wil::SetResultLoggingCallback); use with custom logging
|
||||
__declspec(selectany) void(__stdcall *g_pfnLoggingCallback)(wil::FailureInfo const &failure) WI_NOEXCEPT = nullptr;
|
||||
__declspec(selectany) void(__stdcall *g_pfnLoggingCallback)(wil::FailureInfo const &failure) WI_PFN_NOEXCEPT = nullptr;
|
||||
|
||||
// Desktop/System Only: Module fetch function (automatically setup)
|
||||
__declspec(selectany) PCSTR(__stdcall *g_pfnGetModuleName)() WI_NOEXCEPT = nullptr;
|
||||
__declspec(selectany) PCSTR(__stdcall *g_pfnGetModuleName)() WI_PFN_NOEXCEPT = nullptr;
|
||||
|
||||
// Desktop/System Only: Retrieve address offset and modulename
|
||||
__declspec(selectany) bool(__stdcall *g_pfnGetModuleInformation)(void* address, _Out_opt_ unsigned int* addressOffset, _Out_writes_bytes_opt_(size) char* name, size_t size) WI_NOEXCEPT = nullptr;
|
||||
__declspec(selectany) bool(__stdcall *g_pfnGetModuleInformation)(void* address, _Out_opt_ unsigned int* addressOffset, _Out_writes_bytes_opt_(size) char* name, size_t size) WI_PFN_NOEXCEPT = nullptr;
|
||||
|
||||
// Desktop/System Only: Private module load fail fast function (automatically setup)
|
||||
__declspec(selectany) void(__stdcall *g_pfnFailFastInLoaderCallout)() WI_NOEXCEPT = nullptr;
|
||||
// Called with the expectation that the program will terminate when called inside of a loader callout.
|
||||
// Desktop/System Only: Automatically setup when building Windows (BUILD_WINDOWS defined)
|
||||
__declspec(selectany) void(__stdcall *g_pfnFailFastInLoaderCallout)() WI_PFN_NOEXCEPT = nullptr;
|
||||
|
||||
// Desktop/System Only: Private module load convert NtStatus to HResult (automatically setup)
|
||||
__declspec(selectany) ULONG(__stdcall *g_pfnRtlNtStatusToDosErrorNoTeb)(NTSTATUS) WI_NOEXCEPT = nullptr;
|
||||
// Called to translate an NTSTATUS value to a Win32 error code
|
||||
// Desktop/System Only: Automatically setup when building Windows (BUILD_WINDOWS defined)
|
||||
__declspec(selectany) ULONG(__stdcall *g_pfnRtlNtStatusToDosErrorNoTeb)(NTSTATUS) WI_PFN_NOEXCEPT = nullptr;
|
||||
|
||||
// Desktop/System Only: Private module load to call debug break
|
||||
__declspec(selectany) void(__stdcall *g_pfnDebugBreak)() WI_NOEXCEPT = nullptr;
|
||||
// Desktop/System Only: Call to DebugBreak
|
||||
__declspec(selectany) void(__stdcall *g_pfnDebugBreak)() WI_PFN_NOEXCEPT = nullptr;
|
||||
|
||||
// Private API to determine whether or not termination is happening
|
||||
__declspec(selectany) BOOLEAN(__stdcall *g_pfnRtlDllShutdownInProgress)() WI_NOEXCEPT = nullptr;
|
||||
// Called to determine whether or not termination is happening
|
||||
// Desktop/System Only: Automatically setup when building Windows (BUILD_WINDOWS defined)
|
||||
__declspec(selectany) BOOLEAN(__stdcall *g_pfnDllShutdownInProgress)() WI_PFN_NOEXCEPT = nullptr;
|
||||
__declspec(selectany) bool g_processShutdownInProgress = false;
|
||||
|
||||
// On Desktop/System WINAPI family: dynalink RaiseFailFastException because we may encounter modules
|
||||
|
@ -1174,15 +1195,15 @@ namespace wil
|
|||
__declspec(selectany) HRESULT(__stdcall *g_pfnRunFunctorWithExceptionFilter)(IFunctor& functor, IFunctorHost& host, void* returnAddress) = nullptr;
|
||||
__declspec(selectany) void(__stdcall *g_pfnRethrow)() = nullptr;
|
||||
__declspec(selectany) void(__stdcall *g_pfnThrowResultException)(const FailureInfo& failure) = nullptr;
|
||||
extern "C" __declspec(selectany) HRESULT(__stdcall *g_pfnResultFromCaughtExceptionInternal)(_Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_NOEXCEPT = nullptr;
|
||||
extern "C" __declspec(selectany) HRESULT(__stdcall *g_pfnResultFromCaughtExceptionInternal)(_Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_PFN_NOEXCEPT = nullptr;
|
||||
|
||||
// C++/cx compiled additions
|
||||
extern "C" __declspec(selectany) void(__stdcall *g_pfnThrowPlatformException)(FailureInfo const &failure, PCWSTR debugString) = nullptr;
|
||||
extern "C" __declspec(selectany) _Always_(_Post_satisfies_(return < 0)) HRESULT(__stdcall *g_pfnResultFromCaughtException_WinRt)(_Inout_updates_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_NOEXCEPT = nullptr;
|
||||
extern "C" __declspec(selectany) _Always_(_Post_satisfies_(return < 0)) HRESULT(__stdcall *g_pfnResultFromCaughtException_WinRt)(_Inout_updates_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Out_ bool* isNormalized) WI_PFN_NOEXCEPT = nullptr;
|
||||
__declspec(selectany) _Always_(_Post_satisfies_(return < 0)) HRESULT(__stdcall *g_pfnResultFromKnownExceptions_WinRt)(const DiagnosticsInfo& diagnostics, void* returnAddress, SupportedExceptions supported, IFunctor& functor) = nullptr;
|
||||
|
||||
// Plugin to call RoOriginateError (WIL use only)
|
||||
__declspec(selectany) void(__stdcall *g_pfnOriginateCallback)(wil::FailureInfo const& failure) WI_NOEXCEPT = nullptr;
|
||||
__declspec(selectany) void(__stdcall *g_pfnOriginateCallback)(wil::FailureInfo const& failure) WI_PFN_NOEXCEPT = nullptr;
|
||||
|
||||
enum class ReportFailureOptions
|
||||
{
|
||||
|
@ -1202,7 +1223,7 @@ namespace wil
|
|||
TFunctor&& functor;
|
||||
functor_wrapper_void(TFunctor&& functor_) : functor(wistd::forward<TFunctor>(functor_)) { }
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4702) /* https://microsoft.visualstudio.com/OS/_workitems?id=15917057&fullScreen=false&_a=edit */
|
||||
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
|
||||
HRESULT Run() override
|
||||
{
|
||||
functor();
|
||||
|
@ -1229,7 +1250,7 @@ namespace wil
|
|||
TReturn& retVal;
|
||||
functor_wrapper_other(TFunctor& functor_, TReturn& retval_) : functor(wistd::forward<TFunctor>(functor_)), retVal(retval_) { }
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4702) /* https://microsoft.visualstudio.com/OS/_workitems?id=15917057&fullScreen=false&_a=edit */
|
||||
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
|
||||
HRESULT Run() override
|
||||
{
|
||||
retVal = functor();
|
||||
|
@ -1316,8 +1337,8 @@ namespace wil
|
|||
//*****************************************************************************
|
||||
|
||||
/// @cond
|
||||
#define __FAIL_FAST_ASSERT__(condition) do { if (!(condition)) { __RFF_FN(FailFast_Unexpected)(__RFF_INFO_ONLY(#condition)); } } while (0, 0)
|
||||
#define __FAIL_FAST_IMMEDIATE_ASSERT__(condition) do { if (!(condition)) { wil::FailureInfo failure {}; wil::details::WilFailFast(failure); } } while (0, 0)
|
||||
#define __FAIL_FAST_ASSERT__(condition) do { if (!(condition)) { __RFF_FN(FailFast_Unexpected)(__RFF_INFO_ONLY(#condition)); } } while ((void)0, 0)
|
||||
#define __FAIL_FAST_IMMEDIATE_ASSERT__(condition) do { if (!(condition)) { wil::FailureInfo failure {}; wil::details::WilFailFast(failure); } } while ((void)0, 0)
|
||||
#define __FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(condition) __RFF_FN(FailFast_IfWin32BoolFalse)(__RFF_INFO(#condition) wil::verify_BOOL(condition))
|
||||
|
||||
// A simple ref-counted buffer class. The interface is very similar to shared_ptr<>, only it manages
|
||||
|
@ -1719,7 +1740,7 @@ namespace wil
|
|||
// All successful status codes have only one hresult equivalent, S_OK
|
||||
return S_OK;
|
||||
}
|
||||
if (status == STATUS_NO_MEMORY)
|
||||
if (status == static_cast<NTSTATUS>(STATUS_NO_MEMORY))
|
||||
{
|
||||
// RtlNtStatusToDosErrorNoTeb maps STATUS_NO_MEMORY to the less popular of two Win32 no memory error codes resulting in an unexpected mapping
|
||||
return E_OUTOFMEMORY;
|
||||
|
@ -1843,9 +1864,7 @@ namespace wil
|
|||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4100) // Unused parameter (pszDest)
|
||||
_Post_satisfies_(cchDest > 0 && cchDest <= cchMax) static STRSAFEAPI WilStringValidateDestA(_In_reads_opt_(cchDest) STRSAFE_PCNZCH pszDest, _In_ size_t cchDest, _In_ const size_t cchMax)
|
||||
_Post_satisfies_(cchDest > 0 && cchDest <= cchMax) static STRSAFEAPI WilStringValidateDestA(_In_reads_opt_(cchDest) STRSAFE_PCNZCH /*pszDest*/, _In_ size_t cchDest, _In_ const size_t cchMax)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
if ((cchDest == 0) || (cchDest > cchMax))
|
||||
|
@ -1854,7 +1873,6 @@ namespace wil
|
|||
}
|
||||
return hr;
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
static STRSAFEAPI WilStringVPrintfWorkerA(_Out_writes_(cchDest) _Always_(_Post_z_) STRSAFE_LPSTR pszDest, _In_ _In_range_(1, STRSAFE_MAX_CCH) size_t cchDest, _Always_(_Out_opt_ _Deref_out_range_(<=, cchDest - 1)) size_t* pcchNewDestLength, _In_ _Printf_format_string_ STRSAFE_LPCSTR pszFormat, _In_ va_list argList)
|
||||
{
|
||||
|
@ -1909,7 +1927,7 @@ namespace wil
|
|||
return hr;
|
||||
}
|
||||
|
||||
STRSAFEAPI StringCchPrintfA( _Out_writes_(cchDest) _Always_(_Post_z_) STRSAFE_LPSTR pszDest, _In_ size_t cchDest, _In_ _Printf_format_string_ STRSAFE_LPCSTR pszFormat, ...)
|
||||
__inline HRESULT StringCchPrintfA( _Out_writes_(cchDest) _Always_(_Post_z_) STRSAFE_LPSTR pszDest, _In_ size_t cchDest, _In_ _Printf_format_string_ STRSAFE_LPCSTR pszFormat, ...)
|
||||
{
|
||||
HRESULT hr;
|
||||
hr = wil::details::WilStringValidateDestA(pszDest, cchDest, STRSAFE_MAX_CCH);
|
||||
|
@ -2030,7 +2048,7 @@ namespace wil
|
|||
//! Call this method to determine if process shutdown is in progress (allows avoiding work during dll unload).
|
||||
inline bool ProcessShutdownInProgress()
|
||||
{
|
||||
return (details::g_processShutdownInProgress || (details::g_pfnRtlDllShutdownInProgress ? details::g_pfnRtlDllShutdownInProgress() : false));
|
||||
return (details::g_processShutdownInProgress || (details::g_pfnDllShutdownInProgress ? details::g_pfnDllShutdownInProgress() : false));
|
||||
}
|
||||
|
||||
/** Use this object to wrap an object that wants to prevent its destructor from being run when the process is shutting down,
|
||||
|
@ -2067,7 +2085,7 @@ namespace wil
|
|||
}
|
||||
|
||||
private:
|
||||
unsigned char m_raw[sizeof(T)];
|
||||
alignas(T) unsigned char m_raw[sizeof(T)];
|
||||
};
|
||||
|
||||
/** Use this object to wrap an object that wants to prevent its destructor from being run when the process is shutting down.
|
||||
|
@ -2123,11 +2141,11 @@ namespace wil
|
|||
}
|
||||
|
||||
private:
|
||||
unsigned char m_raw[sizeof(T)];
|
||||
alignas(T) unsigned char m_raw[sizeof(T)];
|
||||
};
|
||||
|
||||
/** Forward your DLLMain to this function so that WIL can have visibility into whether a DLL unload is because
|
||||
of termination or normal unload. Note that when private API usage is enabled, WIL attempts to make this
|
||||
of termination or normal unload. Note that when g_pfnDllShutdownInProgress is set, WIL attempts to make this
|
||||
determination on its own without this callback. Suppressing private APIs requires use of this. */
|
||||
inline void DLLMain(HINSTANCE, DWORD reason, _In_opt_ LPVOID reserved)
|
||||
{
|
||||
|
@ -3143,8 +3161,7 @@ namespace wil
|
|||
// Returns true if a debugger should be considered to be connected.
|
||||
// Modules can force this on through setting g_fIsDebuggerPresent explicitly (useful for live debugging),
|
||||
// they can provide a callback function by setting g_pfnIsDebuggerPresent (useful for kernel debbugging),
|
||||
// and finally the user-mode check (IsDebuggerPrsent) is checked. IsDebuggerPresent is a fast call as it
|
||||
// returns NtCurrentPeb()->BeingDebugged.
|
||||
// and finally the user-mode check (IsDebuggerPrsent) is checked. IsDebuggerPresent is a fast call
|
||||
inline bool IsDebuggerPresent()
|
||||
{
|
||||
return g_fIsDebuggerPresent || ((g_pfnIsDebuggerPresent != nullptr) ? g_pfnIsDebuggerPresent() : (::IsDebuggerPresent() != FALSE));
|
||||
|
@ -3299,7 +3316,6 @@ namespace wil
|
|||
er.ExceptionInformation[0] = FAST_FAIL_FATAL_APP_EXIT; // see winnt.h, generated from minkernel\published\base\ntrtl_x.w
|
||||
if (failure.returnAddress == 0) // FailureInfo does not have _ReturnAddress, have RaiseFailFastException generate it
|
||||
{
|
||||
// http://osgvsowi/17364039 - confirm with !analyze team that this is the best we can do in this case
|
||||
// passing ExceptionCode 0xC0000409 and one param with FAST_FAIL_APP_EXIT will use existing
|
||||
// !analyze functionality to crawl the stack looking for the HRESULT
|
||||
// don't pass a 0 HRESULT in param 1 because that will result in worse bucketing.
|
||||
|
|
|
@ -1,3 +1,14 @@
|
|||
//*********************************************************
|
||||
//
|
||||
// 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
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#ifndef __WIL_SAFECAST_INCLUDED
|
||||
#define __WIL_SAFECAST_INCLUDED
|
||||
|
||||
|
@ -291,9 +300,9 @@ namespace wil
|
|||
typename OldT,
|
||||
wistd::enable_if_t<details::is_supported_unsafe_cast_v<NewT, OldT>, int> = 0
|
||||
>
|
||||
NewT safe_cast_nothrow(const OldT var)
|
||||
NewT safe_cast_nothrow(const OldT /*var*/)
|
||||
{
|
||||
static_assert(false, "This cast has the potential to fail, use the two parameter safe_cast_nothrow instead");
|
||||
static_assert(!wistd::is_same_v<NewT, NewT>, "This cast has the potential to fail, use the two parameter safe_cast_nothrow instead");
|
||||
}
|
||||
|
||||
// This conversion is always safe, therefore a static_cast is fine.
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
|
||||
//*********************************************************
|
||||
//
|
||||
// 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
|
||||
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#ifndef __WIL_TOKEN_HELPERS_INCLUDED
|
||||
#define __WIL_TOKEN_HELPERS_INCLUDED
|
||||
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
|
||||
//*********************************************************
|
||||
//
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// This code is licensed under the MIT License.
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
// PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
//
|
||||
//*********************************************************
|
||||
#ifndef __WIL_WIN32_HELPERS_INCLUDED
|
||||
#define __WIL_WIN32_HELPERS_INCLUDED
|
||||
|
||||
|
@ -158,7 +167,7 @@ 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 //osgvsowi/14483919
|
||||
// These updates and unit tests are tracked by https://github.com/Microsoft/wil/issues/3
|
||||
template <typename string_type, size_t stackBufferLength = 256>
|
||||
HRESULT QueryFullProcessImageNameW(HANDLE processHandle, _In_ DWORD flags, string_type& result) WI_NOEXCEPT
|
||||
{
|
||||
|
@ -401,7 +410,7 @@ namespace wil
|
|||
}
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4702) /* https://microsoft.visualstudio.com/OS/_workitems?id=15917057&fullScreen=false&_a=edit */
|
||||
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
|
||||
void success()
|
||||
{
|
||||
m_flags = 0;
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
|
||||
//*********************************************************
|
||||
//
|
||||
// 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_WINRT_INCLUDED
|
||||
#define __WIL_WINRT_INCLUDED
|
||||
|
||||
|
@ -75,68 +84,6 @@ namespace wil
|
|||
template <bool InhibitStringArrays, bool IgnoreCase>
|
||||
struct hstring_compare
|
||||
{
|
||||
template <typename LhsT, typename RhsT>
|
||||
static auto compare(LhsT&& lhs, RhsT&& rhs) ->
|
||||
decltype(get_buffer(lhs, wistd::declval<UINT32*>()), get_buffer(rhs, wistd::declval<UINT32*>()), int())
|
||||
{
|
||||
UINT32 lhsLength;
|
||||
UINT32 rhsLength;
|
||||
auto lhsBuffer = get_buffer(wistd::forward<LhsT>(lhs), &lhsLength);
|
||||
auto rhsBuffer = get_buffer(wistd::forward<RhsT>(rhs), &rhsLength);
|
||||
|
||||
const auto result = ::CompareStringOrdinal(
|
||||
lhsBuffer,
|
||||
lhsLength,
|
||||
rhsBuffer,
|
||||
rhsLength,
|
||||
IgnoreCase ? TRUE : FALSE);
|
||||
NT_ASSERT(result != 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename LhsT, typename RhsT>
|
||||
static auto equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
|
||||
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
||||
{
|
||||
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) == CSTR_EQUAL;
|
||||
}
|
||||
|
||||
template <typename LhsT, typename RhsT>
|
||||
static auto not_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
|
||||
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
||||
{
|
||||
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) != CSTR_EQUAL;
|
||||
}
|
||||
|
||||
template <typename LhsT, typename RhsT>
|
||||
static auto less(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
|
||||
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
||||
{
|
||||
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) == CSTR_LESS_THAN;
|
||||
}
|
||||
|
||||
template <typename LhsT, typename RhsT>
|
||||
static auto less_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
|
||||
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
||||
{
|
||||
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) != CSTR_GREATER_THAN;
|
||||
}
|
||||
|
||||
template <typename LhsT, typename RhsT>
|
||||
static auto greater(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
|
||||
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
||||
{
|
||||
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) == CSTR_GREATER_THAN;
|
||||
}
|
||||
|
||||
template <typename LhsT, typename RhsT>
|
||||
static auto greater_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
|
||||
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
||||
{
|
||||
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) != CSTR_LESS_THAN;
|
||||
}
|
||||
|
||||
// get_buffer returns the string buffer and length for the supported string types
|
||||
static const wchar_t* get_buffer(HSTRING hstr, UINT32* length) WI_NOEXCEPT
|
||||
{
|
||||
|
@ -208,6 +155,68 @@ namespace wil
|
|||
return str.c_str();
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename LhsT, typename RhsT>
|
||||
static auto compare(LhsT&& lhs, RhsT&& rhs) ->
|
||||
decltype(get_buffer(lhs, wistd::declval<UINT32*>()), get_buffer(rhs, wistd::declval<UINT32*>()), int())
|
||||
{
|
||||
UINT32 lhsLength;
|
||||
UINT32 rhsLength;
|
||||
auto lhsBuffer = get_buffer(wistd::forward<LhsT>(lhs), &lhsLength);
|
||||
auto rhsBuffer = get_buffer(wistd::forward<RhsT>(rhs), &rhsLength);
|
||||
|
||||
const auto result = ::CompareStringOrdinal(
|
||||
lhsBuffer,
|
||||
lhsLength,
|
||||
rhsBuffer,
|
||||
rhsLength,
|
||||
IgnoreCase ? TRUE : FALSE);
|
||||
WI_ASSERT(result != 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename LhsT, typename RhsT>
|
||||
static auto equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
|
||||
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
||||
{
|
||||
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) == CSTR_EQUAL;
|
||||
}
|
||||
|
||||
template <typename LhsT, typename RhsT>
|
||||
static auto not_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
|
||||
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
||||
{
|
||||
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) != CSTR_EQUAL;
|
||||
}
|
||||
|
||||
template <typename LhsT, typename RhsT>
|
||||
static auto less(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
|
||||
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
||||
{
|
||||
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) == CSTR_LESS_THAN;
|
||||
}
|
||||
|
||||
template <typename LhsT, typename RhsT>
|
||||
static auto less_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
|
||||
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
||||
{
|
||||
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) != CSTR_GREATER_THAN;
|
||||
}
|
||||
|
||||
template <typename LhsT, typename RhsT>
|
||||
static auto greater(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
|
||||
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
||||
{
|
||||
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) == CSTR_GREATER_THAN;
|
||||
}
|
||||
|
||||
template <typename LhsT, typename RhsT>
|
||||
static auto greater_equals(LhsT&& lhs, RhsT&& rhs) WI_NOEXCEPT ->
|
||||
decltype(compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)), bool())
|
||||
{
|
||||
return compare(wistd::forward<LhsT>(lhs), wistd::forward<RhsT>(rhs)) != CSTR_LESS_THAN;
|
||||
}
|
||||
};
|
||||
}
|
||||
/// @endcond
|
||||
|
@ -426,7 +435,7 @@ namespace wil
|
|||
template<typename T, typename Enable = void> struct MapToSmartType
|
||||
{
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4702) /* https://microsoft.visualstudio.com/OS/_workitems?id=15917057&fullScreen=false&_a=edit */
|
||||
#pragma warning(disable:4702) // https://github.com/Microsoft/wil/issues/2
|
||||
struct type // T holder
|
||||
{
|
||||
type() {};
|
||||
|
@ -439,6 +448,7 @@ namespace wil
|
|||
// In case of absense of T::operator=(T&&) a call to T::operator=(const T&) will happen
|
||||
T&& Get() { return wistd::move(m_value); }
|
||||
|
||||
HRESULT CopyTo(T* result) const { *result = m_value; return S_OK; }
|
||||
T* GetAddressOf() { return &m_value; }
|
||||
T* ReleaseAndGetAddressOf() { return &m_value; }
|
||||
T* operator&() { return &m_value; }
|
||||
|
@ -727,8 +737,8 @@ namespace wil
|
|||
vector_range_nothrow& operator=(const vector_range_nothrow&) = delete;
|
||||
|
||||
vector_range_nothrow(vector_range_nothrow&& other) :
|
||||
m_v(other.m_v), m_result(other.m_result), m_resultStorage(other.m_resultStorage),
|
||||
m_size(other.m_size), m_currentElement(wistd::move(other.m_currentElement))
|
||||
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))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -897,7 +907,7 @@ namespace wil
|
|||
}
|
||||
|
||||
// for end()
|
||||
iterable_iterator(int currentIndex) : m_i(-1)
|
||||
iterable_iterator(int /*currentIndex*/) : m_i(-1)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1244,6 +1254,7 @@ namespace details
|
|||
return wistd::forward<TFunc>(func)(wistd::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
template <typename TResult, typename TFunc, typename ...Args,
|
||||
typename wistd::enable_if<wistd::is_same<void, TResult>::value, int>::type = 0>
|
||||
HRESULT CallAndHandleErrorsWithReturnType(TFunc&& func, Args&&... args)
|
||||
|
@ -1255,6 +1266,7 @@ namespace details
|
|||
CATCH_RETURN();
|
||||
return S_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename TFunc, typename ...Args>
|
||||
HRESULT CallAndHandleErrors(TFunc&& func, Args&&... args)
|
||||
|
@ -2041,8 +2053,8 @@ namespace details
|
|||
#define WI_MakeUniqueWinRtEventToken(_event, _object, _handler) \
|
||||
wil::details::make_unique_winrt_event_token<wil::err_exception_policy>( \
|
||||
_object, \
|
||||
&wistd::remove_pointer<decltype(##_object##)>::type::add_##_event, \
|
||||
&wistd::remove_pointer<decltype(##_object##)>::type::remove_##_event, \
|
||||
&wistd::remove_pointer<decltype(_object)>::type::add_##_event, \
|
||||
&wistd::remove_pointer<decltype(_object)>::type::remove_##_event, \
|
||||
_handler)
|
||||
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
@ -2050,16 +2062,16 @@ namespace details
|
|||
#define WI_MakeUniqueWinRtEventTokenNoThrow(_event, _object, _handler, _token_reference) \
|
||||
wil::details::make_unique_winrt_event_token( \
|
||||
_object, \
|
||||
&wistd::remove_pointer<decltype(##_object##)>::type::add_##_event, \
|
||||
&wistd::remove_pointer<decltype(##_object##)>::type::remove_##_event, \
|
||||
&wistd::remove_pointer<decltype(_object)>::type::add_##_event, \
|
||||
&wistd::remove_pointer<decltype(_object)>::type::remove_##_event, \
|
||||
_handler, \
|
||||
_token_reference)
|
||||
|
||||
#define WI_MakeUniqueWinRtEventTokenFailFast(_event, _object, _handler) \
|
||||
wil::details::make_unique_winrt_event_token<wil::err_failfast_policy>( \
|
||||
_object, \
|
||||
&wistd::remove_pointer<decltype(##_object##)>::type::add_##_event, \
|
||||
&wistd::remove_pointer<decltype(##_object##)>::type::remove_##_event, \
|
||||
&wistd::remove_pointer<decltype(_object)>::type::add_##_event, \
|
||||
&wistd::remove_pointer<decltype(_object)>::type::remove_##_event, \
|
||||
_handler)
|
||||
|
||||
#pragma endregion // EventRegistrationToken RAII wrapper
|
||||
|
|
|
@ -1,3 +1,13 @@
|
|||
// -*- C++ -*-
|
||||
//===--------------------------- __config ---------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// STL common functionality
|
||||
//
|
||||
// Some aspects of STL are core language concepts that should be used from all C++ code, regardless
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
|
||||
// DO NOT add *any* additional includes to this file -- there should be no dependencies from its usage
|
||||
#include "wistd_memory.h"
|
||||
#include <intrin.h> // For __fastfail
|
||||
|
||||
#if !defined(__WI_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
|
||||
#pragma GCC system_header
|
||||
|
@ -128,7 +129,7 @@ namespace wistd // ("Windows Implementation" std)
|
|||
__WI_LIBCPP_NORETURN inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||
void __throw_bad_function_call()
|
||||
{
|
||||
__fastfail(FAST_FAIL_FATAL_APP_EXIT);
|
||||
__fastfail(7); // FAST_FAIL_FATAL_APP_EXIT
|
||||
}
|
||||
|
||||
template<class _Fp> class __WI_LIBCPP_TEMPLATE_VIS function; // undefined
|
||||
|
@ -191,7 +192,7 @@ namespace wistd // ("Windows Implementation" std)
|
|||
__WI_LIBCPP_INLINE_VISIBILITY __base() {}
|
||||
__WI_LIBCPP_INLINE_VISIBILITY virtual ~__base() {}
|
||||
virtual void __clone(__base*) const = 0;
|
||||
virtual void __move(__base*) WI_NOEXCEPT = 0;
|
||||
virtual void __move(__base*) = 0;
|
||||
virtual void destroy() WI_NOEXCEPT = 0;
|
||||
virtual _Rp operator()(_ArgTypes&& ...) = 0;
|
||||
};
|
||||
|
@ -213,7 +214,7 @@ namespace wistd // ("Windows Implementation" std)
|
|||
: __f_(__f) {}
|
||||
|
||||
virtual void __clone(__base<_Rp(_ArgTypes...)>*) const;
|
||||
virtual void __move(__base<_Rp(_ArgTypes...)>*) WI_NOEXCEPT;
|
||||
virtual void __move(__base<_Rp(_ArgTypes...)>*);
|
||||
virtual void destroy() WI_NOEXCEPT;
|
||||
virtual _Rp operator()(_ArgTypes&& ... __arg);
|
||||
};
|
||||
|
@ -227,7 +228,7 @@ namespace wistd // ("Windows Implementation" std)
|
|||
|
||||
template<class _Fp, class _Rp, class ..._ArgTypes>
|
||||
void
|
||||
__func<_Fp, _Rp(_ArgTypes...)>::__move(__base<_Rp(_ArgTypes...)>* __p) WI_NOEXCEPT
|
||||
__func<_Fp, _Rp(_ArgTypes...)>::__move(__base<_Rp(_ArgTypes...)>* __p)
|
||||
{
|
||||
::new (__p) __func(wistd::move(__f_));
|
||||
}
|
||||
|
@ -301,12 +302,12 @@ namespace wistd // ("Windows Implementation" std)
|
|||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
function(nullptr_t) WI_NOEXCEPT : __f_(0) {}
|
||||
function(const function&);
|
||||
function(function&&) WI_NOEXCEPT;
|
||||
function(function&&);
|
||||
template<class _Fp, class = _EnableIfCallable<_Fp>>
|
||||
function(_Fp);
|
||||
|
||||
function& operator=(const function&);
|
||||
function& operator=(function&&) WI_NOEXCEPT;
|
||||
function& operator=(function&&);
|
||||
function& operator=(nullptr_t) WI_NOEXCEPT;
|
||||
template<class _Fp, class = _EnableIfCallable<_Fp>>
|
||||
function& operator=(_Fp&&);
|
||||
|
@ -314,7 +315,7 @@ namespace wistd // ("Windows Implementation" std)
|
|||
~function();
|
||||
|
||||
// function modifiers:
|
||||
void swap(function&) WI_NOEXCEPT;
|
||||
void swap(function&);
|
||||
|
||||
// function capacity:
|
||||
__WI_LIBCPP_INLINE_VISIBILITY
|
||||
|
@ -346,7 +347,7 @@ namespace wistd // ("Windows Implementation" std)
|
|||
}
|
||||
|
||||
template<class _Rp, class ..._ArgTypes>
|
||||
function<_Rp(_ArgTypes...)>::function(function&& __f) WI_NOEXCEPT
|
||||
function<_Rp(_ArgTypes...)>::function(function&& __f)
|
||||
{
|
||||
if (__f.__f_ == 0)
|
||||
__f_ = 0;
|
||||
|
@ -388,7 +389,7 @@ namespace wistd // ("Windows Implementation" std)
|
|||
|
||||
template<class _Rp, class ..._ArgTypes>
|
||||
function<_Rp(_ArgTypes...)>&
|
||||
function<_Rp(_ArgTypes...)>::operator=(function&& __f) WI_NOEXCEPT
|
||||
function<_Rp(_ArgTypes...)>::operator=(function&& __f)
|
||||
{
|
||||
*this = nullptr;
|
||||
if (__f.__f_)
|
||||
|
@ -438,7 +439,7 @@ namespace wistd // ("Windows Implementation" std)
|
|||
|
||||
template<class _Rp, class ..._ArgTypes>
|
||||
void
|
||||
function<_Rp(_ArgTypes...)>::swap(function& __f) WI_NOEXCEPT
|
||||
function<_Rp(_ArgTypes...)>::swap(function& __f)
|
||||
{
|
||||
if (wistd::addressof(__f) == this)
|
||||
return;
|
||||
|
@ -506,13 +507,13 @@ namespace wistd // ("Windows Implementation" std)
|
|||
template <class _Rp, class... _ArgTypes>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
swap(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y) WI_NOEXCEPT
|
||||
swap(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y)
|
||||
{return __x.swap(__y);}
|
||||
|
||||
template <class _Rp, class... _ArgTypes>
|
||||
inline __WI_LIBCPP_INLINE_VISIBILITY
|
||||
void
|
||||
swap_wil(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y) WI_NOEXCEPT
|
||||
swap_wil(function<_Rp(_ArgTypes...)>& __x, function<_Rp(_ArgTypes...)>& __y)
|
||||
{return __x.swap(__y);}
|
||||
|
||||
#else // __WI_LIBCPP_CXX03_LANG
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
|
||||
//*********************************************************
|
||||
//
|
||||
// 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
|
||||
|
||||
|
|
|
@ -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,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,154 @@
|
|||
@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^>] [--fast]
|
||||
echo [-b^|--build-type ^<debug^|release^|relwithdebinfo^|minsizerel^>] [-v^|--version X.Y.Z]
|
||||
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 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 & 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 & 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 & 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 & 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 & 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 & 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 & exit /B 1
|
||||
if /I "%~2"=="" echo ERROR: Version string missing & 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
|
||||
exit /B 1
|
||||
|
||||
:execute
|
||||
:: Check for conflicting arguments
|
||||
if "%COMPILER%"=="clang" (
|
||||
if "%GENERATOR%"=="msbuild" echo ERROR: Cannot use Clang 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
|
||||
|
||||
:: 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 %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
|
||||
|
||||
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 architecture... %Platform%
|
||||
echo Using build type..... %BUILD_TYPE%
|
||||
echo Using build root..... %CD%
|
||||
echo.
|
||||
cmake %CMAKE_ARGS% ..\..
|
||||
popd
|
||||
|
||||
goto :eof
|
|
@ -0,0 +1,17 @@
|
|||
@echo off
|
||||
|
||||
:: NOTE: Architecture is picked up from the command window, so we can't control that here :(
|
||||
|
||||
:: TODO: https://github.com/Microsoft/wil/issues/7 - There's currently a bug where Clang and/or the linker chokes when
|
||||
:: trying to compile the tests for 32-bit debug, so skip for now
|
||||
if "%Platform%"=="x86" goto :skip_clang_x86_debug
|
||||
call %~dp0\init.cmd -c clang -g ninja -b debug %*
|
||||
if %ERRORLEVEL% NEQ 0 ( goto :eof )
|
||||
:skip_clang_x86_debug
|
||||
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)
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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,74 @@
|
|||
|
||||
#include "catch.hpp"
|
||||
|
||||
#include <wil/cppwinrt.h>
|
||||
|
||||
TEST_CASE("CppWinRTTests::WilToCppWinRTExceptionTranslationTest", "[cppwinrt]")
|
||||
{
|
||||
auto test = [](HRESULT hr)
|
||||
{
|
||||
try
|
||||
{
|
||||
THROW_HR(hr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
REQUIRE(hr == winrt::to_hresult());
|
||||
}
|
||||
};
|
||||
|
||||
test(E_UNEXPECTED);
|
||||
test(E_ACCESSDENIED);
|
||||
test(E_INVALIDARG);
|
||||
test(E_HANDLE);
|
||||
test(E_OUTOFMEMORY);
|
||||
}
|
||||
|
||||
TEST_CASE("CppWinRTTests::CppWinRTToWilExceptionTranslationTest", "[cppwinrt]")
|
||||
{
|
||||
auto test = [](HRESULT hr)
|
||||
{
|
||||
try
|
||||
{
|
||||
winrt::check_hresult(hr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
REQUIRE(hr == wil::ResultFromCaughtException());
|
||||
}
|
||||
};
|
||||
|
||||
test(E_UNEXPECTED);
|
||||
test(E_ACCESSDENIED);
|
||||
test(E_INVALIDARG);
|
||||
test(E_HANDLE);
|
||||
test(E_OUTOFMEMORY);
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
// Anything from SupportedExceptions::Known or SupportedExceptions::All should give back the same HRESULT
|
||||
test(E_UNEXPECTED, wil::SupportedExceptions::Known);
|
||||
test(E_ACCESSDENIED, wil::SupportedExceptions::Known);
|
||||
test(E_INVALIDARG, wil::SupportedExceptions::All);
|
||||
test(E_HANDLE, wil::SupportedExceptions::All);
|
||||
|
||||
// OOM gets translated to bad_alloc, which should always give back E_OUTOFMEMORY
|
||||
test(E_OUTOFMEMORY, wil::SupportedExceptions::All);
|
||||
test(E_OUTOFMEMORY, wil::SupportedExceptions::Known);
|
||||
test(E_OUTOFMEMORY, wil::SupportedExceptions::ThrownOrAlloc);
|
||||
|
||||
// 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);
|
||||
}
|
|
@ -0,0 +1,400 @@
|
|||
#pragma once
|
||||
|
||||
#include <wil/resource.h>
|
||||
#include <windows.foundation.h>
|
||||
#include <wrl/implements.h>
|
||||
#include <wrl/wrappers/corewrappers.h>
|
||||
|
||||
template <typename T>
|
||||
struct WinRTStorage
|
||||
{
|
||||
T value = {};
|
||||
|
||||
HRESULT CopyTo(T* result)
|
||||
{
|
||||
*result = value;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Set(T val)
|
||||
{
|
||||
value = val;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void Destroy(T&)
|
||||
{
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
}
|
||||
|
||||
bool Equals(T val)
|
||||
{
|
||||
// NOTE: Padding can through this off, but this isn't intended to be a robust solution...
|
||||
return memcmp(&value, &val, sizeof(T)) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct WinRTStorage<HSTRING>
|
||||
{
|
||||
Microsoft::WRL::Wrappers::HString value;
|
||||
|
||||
HRESULT CopyTo(HSTRING* result)
|
||||
{
|
||||
return value.CopyTo(result);
|
||||
}
|
||||
|
||||
HRESULT Set(HSTRING val)
|
||||
{
|
||||
return value.Set(val);
|
||||
}
|
||||
|
||||
static void Destroy(HSTRING& val)
|
||||
{
|
||||
::WindowsDeleteString(val);
|
||||
val = nullptr;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
value = {};
|
||||
}
|
||||
|
||||
bool Equals(HSTRING val)
|
||||
{
|
||||
return value == val;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct WinRTStorage<T*>
|
||||
{
|
||||
Microsoft::WRL::ComPtr<T> value;
|
||||
|
||||
HRESULT CopyTo(T** result)
|
||||
{
|
||||
*result = Microsoft::WRL::ComPtr<T>(value).Detach();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Set(T* val)
|
||||
{
|
||||
value = val;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void Destroy(T*& val)
|
||||
{
|
||||
val->Release();
|
||||
val = nullptr;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
value.Reset();
|
||||
}
|
||||
|
||||
bool Equals(T* val)
|
||||
{
|
||||
return value.Get() == val;
|
||||
}
|
||||
};
|
||||
|
||||
// Very minimal IAsyncOperation implementation that gives calling tests control over when it completes
|
||||
template <typename Logical, typename Abi = Logical>
|
||||
struct FakeAsyncOperation : Microsoft::WRL::RuntimeClass<
|
||||
ABI::Windows::Foundation::IAsyncInfo,
|
||||
ABI::Windows::Foundation::IAsyncOperation<Logical>>
|
||||
{
|
||||
using Handler = ABI::Windows::Foundation::IAsyncOperationCompletedHandler<Logical>;
|
||||
|
||||
// IAsyncInfo
|
||||
IFACEMETHODIMP get_Id(unsigned int*) override
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP get_Status(AsyncStatus* status) override
|
||||
{
|
||||
auto lock = m_lock.lock_shared();
|
||||
*status = m_status;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP get_ErrorCode(HRESULT* errorCode) override
|
||||
{
|
||||
auto lock = m_lock.lock_shared();
|
||||
*errorCode = m_result;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP Cancel() override
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP Close() override
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
// IAsyncOperation
|
||||
IFACEMETHODIMP put_Completed(Handler* handler) override
|
||||
{
|
||||
bool invoke = false;
|
||||
{
|
||||
auto lock = m_lock.lock_exclusive();
|
||||
if (m_handler)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
m_handler = handler;
|
||||
invoke = m_status != ABI::Windows::Foundation::AsyncStatus::Started;
|
||||
}
|
||||
|
||||
if (invoke)
|
||||
{
|
||||
handler->Invoke(this, m_status);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP get_Completed(Handler** handler) override
|
||||
{
|
||||
auto lock = m_lock.lock_shared();
|
||||
*handler = Microsoft::WRL::ComPtr<Handler>(m_handler).Detach();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP GetResults(Abi* results) override
|
||||
{
|
||||
return m_storage.CopyTo(results);
|
||||
}
|
||||
|
||||
// Test functions
|
||||
void Complete(HRESULT hr, Abi result)
|
||||
{
|
||||
using namespace ABI::Windows::Foundation;
|
||||
Handler* handler = nullptr;
|
||||
{
|
||||
auto lock = m_lock.lock_exclusive();
|
||||
if (m_status == AsyncStatus::Started)
|
||||
{
|
||||
m_result = hr;
|
||||
m_storage.Set(result);
|
||||
m_status = SUCCEEDED(hr) ? AsyncStatus::Completed : AsyncStatus::Error;
|
||||
handler = m_handler.Get();
|
||||
}
|
||||
}
|
||||
|
||||
if (handler)
|
||||
{
|
||||
handler->Invoke(this, m_status);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
wil::srwlock m_lock;
|
||||
Microsoft::WRL::ComPtr<Handler> m_handler;
|
||||
ABI::Windows::Foundation::AsyncStatus m_status = ABI::Windows::Foundation::AsyncStatus::Started;
|
||||
HRESULT m_result = S_OK;
|
||||
WinRTStorage<Abi> m_storage;
|
||||
};
|
||||
|
||||
template <typename Logical, typename Abi = Logical, size_t MaxSize = 42>
|
||||
struct FakeVector : Microsoft::WRL::RuntimeClass<
|
||||
ABI::Windows::Foundation::Collections::IVector<Logical>,
|
||||
ABI::Windows::Foundation::Collections::IVectorView<Logical>>
|
||||
{
|
||||
// IVector
|
||||
IFACEMETHODIMP GetAt(unsigned index, Abi* item) override
|
||||
{
|
||||
if (index >= m_size)
|
||||
{
|
||||
return E_BOUNDS;
|
||||
}
|
||||
|
||||
return m_data[index].CopyTo(item);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP get_Size(unsigned* size) override
|
||||
{
|
||||
*size = static_cast<unsigned>(m_size);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP GetView(ABI::Windows::Foundation::Collections::IVectorView<Logical>** view) override
|
||||
{
|
||||
this->AddRef();
|
||||
*view = this;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP IndexOf(Abi value, unsigned* index, boolean* found) override
|
||||
{
|
||||
for (size_t i = 0; i < m_size; ++i)
|
||||
{
|
||||
if (m_data[i].Equals(value))
|
||||
{
|
||||
*index = static_cast<unsigned>(i);
|
||||
*found = true;
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
*index = 0;
|
||||
*found = false;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP SetAt(unsigned index, Abi item) override
|
||||
{
|
||||
if (index >= m_size)
|
||||
{
|
||||
return E_BOUNDS;
|
||||
}
|
||||
|
||||
return m_data[index].Set(item);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP InsertAt(unsigned index, Abi item) override
|
||||
{
|
||||
// Insert at the end and swap it into place
|
||||
if (index > m_size)
|
||||
{
|
||||
return E_BOUNDS;
|
||||
}
|
||||
|
||||
auto hr = Append(item);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
for (size_t i = m_size - 1; i > index; --i)
|
||||
{
|
||||
wistd::swap_wil(m_data[i], m_data[i - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP RemoveAt(unsigned index) override
|
||||
{
|
||||
if (index >= m_size)
|
||||
{
|
||||
return E_BOUNDS;
|
||||
}
|
||||
|
||||
for (size_t i = index + 1; i < m_size; ++i)
|
||||
{
|
||||
wistd::swap_wil(m_data[i - 1], m_data[i]);
|
||||
}
|
||||
|
||||
m_data[--m_size].Reset();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP Append(Abi item) override
|
||||
{
|
||||
if (m_size > MaxSize)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
auto hr = m_data[m_size].Set(item);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
++m_size;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP RemoveAtEnd() override
|
||||
{
|
||||
if (m_size == 0)
|
||||
{
|
||||
return E_BOUNDS;
|
||||
}
|
||||
|
||||
m_data[--m_size].Reset();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP Clear() override
|
||||
{
|
||||
for (size_t i = 0; i < m_size; ++i)
|
||||
{
|
||||
m_data[i].Reset();
|
||||
}
|
||||
|
||||
m_size = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP GetMany(unsigned startIndex, unsigned capacity, Abi* value, unsigned* actual) override
|
||||
{
|
||||
*actual = 0;
|
||||
if (startIndex >= m_size)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
auto count = m_size - startIndex;
|
||||
count = (count > capacity) ? capacity : count;
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
unsigned i = 0;
|
||||
for (; (i < count) && SUCCEEDED(hr); ++i)
|
||||
{
|
||||
hr = m_data[startIndex + i].CopyTo(value + i);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
*actual = static_cast<unsigned>(count);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (i--)
|
||||
{
|
||||
WinRTStorage<Abi>::Destroy(value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP ReplaceAll(unsigned count, Abi* value) override
|
||||
{
|
||||
if (count > MaxSize)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
Clear();
|
||||
|
||||
HRESULT hr = S_OK;
|
||||
for (size_t i = 0; (i < count) && SUCCEEDED(hr); ++i)
|
||||
{
|
||||
hr = m_data[i].Set(value[i]);
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
size_t m_size = 0;
|
||||
WinRTStorage<Abi> m_data[MaxSize];
|
||||
};
|
|
@ -0,0 +1,448 @@
|
|||
|
||||
#include <windows.h>
|
||||
#include <winstring.h> // For wil::unique_hstring
|
||||
|
||||
#include <wil/common.h>
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
// TODO: str_raw_ptr is not two-phase name lookup clean (https://github.com/Microsoft/wil/issues/8)
|
||||
namespace wil
|
||||
{
|
||||
PCWSTR str_raw_ptr(HSTRING);
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
PCWSTR str_raw_ptr(const std::wstring&);
|
||||
#endif
|
||||
}
|
||||
|
||||
#include <wil/filesystem.h>
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
#include <wil/stl.h> // For std::wstring string_maker
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
|
||||
bool DirectoryExists(_In_ PCWSTR path)
|
||||
{
|
||||
DWORD dwAttrib = GetFileAttributesW(path);
|
||||
|
||||
return (dwAttrib != INVALID_FILE_ATTRIBUTES &&
|
||||
(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
|
||||
}
|
||||
|
||||
TEST_CASE("FileSystemTests::CreateDirectory", "[filesystem]")
|
||||
{
|
||||
wchar_t basePath[MAX_PATH];
|
||||
REQUIRE(GetTempPathW(ARRAYSIZE(basePath), basePath));
|
||||
REQUIRE_SUCCEEDED(PathCchAppend(basePath, ARRAYSIZE(basePath), L"FileSystemTests"));
|
||||
|
||||
REQUIRE_FALSE(DirectoryExists(basePath));
|
||||
REQUIRE(SUCCEEDED(wil::CreateDirectoryDeepNoThrow(basePath)));
|
||||
REQUIRE(DirectoryExists(basePath));
|
||||
|
||||
auto scopeGuard = wil::scope_exit([&]
|
||||
{
|
||||
REQUIRE_SUCCEEDED(wil::RemoveDirectoryRecursiveNoThrow(basePath));
|
||||
});
|
||||
|
||||
PCWSTR relativeTestPath = L"folder1\\folder2\\folder3\\folder4\\folder5\\folder6\\folder7\\folder8";
|
||||
wchar_t absoluteTestPath[MAX_PATH];
|
||||
REQUIRE_SUCCEEDED(StringCchCopyW(absoluteTestPath, ARRAYSIZE(absoluteTestPath), basePath));
|
||||
REQUIRE_SUCCEEDED(PathCchAppend(absoluteTestPath, ARRAYSIZE(absoluteTestPath), relativeTestPath));
|
||||
REQUIRE_FALSE(DirectoryExists(absoluteTestPath));
|
||||
REQUIRE_SUCCEEDED(wil::CreateDirectoryDeepNoThrow(absoluteTestPath));
|
||||
|
||||
PCWSTR invalidCharsPath = L"Bad?Char|";
|
||||
wchar_t absoluteInvalidPath[MAX_PATH];
|
||||
REQUIRE_SUCCEEDED(StringCchCopyW(absoluteInvalidPath, ARRAYSIZE(absoluteInvalidPath), basePath));
|
||||
REQUIRE_SUCCEEDED(PathCchAppend(absoluteInvalidPath, ARRAYSIZE(absoluteInvalidPath), invalidCharsPath));
|
||||
REQUIRE_FALSE(DirectoryExists(absoluteInvalidPath));
|
||||
REQUIRE_FALSE(SUCCEEDED(wil::CreateDirectoryDeepNoThrow(absoluteInvalidPath)));
|
||||
|
||||
PCWSTR testPath3 = L"folder1\\folder2\\folder3";
|
||||
wchar_t absoluteTestPath3[MAX_PATH];
|
||||
REQUIRE_SUCCEEDED(StringCchCopyW(absoluteTestPath3, ARRAYSIZE(absoluteTestPath3), basePath));
|
||||
REQUIRE_SUCCEEDED(PathCchAppend(absoluteTestPath3, ARRAYSIZE(absoluteTestPath3), testPath3));
|
||||
REQUIRE(DirectoryExists(absoluteTestPath3));
|
||||
|
||||
PCWSTR testPath4 = L"folder1\\folder2\\folder3\\folder4";
|
||||
wchar_t absoluteTestPath4[MAX_PATH];
|
||||
REQUIRE_SUCCEEDED(StringCchCopyW(absoluteTestPath4, ARRAYSIZE(absoluteTestPath4), basePath));
|
||||
REQUIRE_SUCCEEDED(PathCchAppend(absoluteTestPath4, ARRAYSIZE(absoluteTestPath4), testPath4));
|
||||
REQUIRE(DirectoryExists(absoluteTestPath4));
|
||||
|
||||
REQUIRE_SUCCEEDED(wil::RemoveDirectoryRecursiveNoThrow(absoluteTestPath3, wil::RemoveDirectoryOptions::KeepRootDirectory));
|
||||
REQUIRE(DirectoryExists(absoluteTestPath3));
|
||||
REQUIRE_FALSE(DirectoryExists(absoluteTestPath4));
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
// 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
|
||||
// that are in the non-normalized form.
|
||||
TEST_CASE("FileSystemTests::VerifyRemoveDirectoryRecursiveCanDeleteFoldersWithNonNormalizedNames", "[filesystem]")
|
||||
{
|
||||
// Extended length paths can access files with non-normalized names.
|
||||
// This function creates a path with that ability.
|
||||
auto CreatePathThatCanAccessNonNormalizedNames = [](PCWSTR root, PCWSTR name)
|
||||
{
|
||||
wil::unique_hlocal_string path;
|
||||
THROW_IF_FAILED(PathAllocCombine(root, name, PATHCCH_DO_NOT_NORMALIZE_SEGMENTS | PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH, &path));
|
||||
REQUIRE(wil::is_extended_length_path(path.get()));
|
||||
return path;
|
||||
};
|
||||
|
||||
// Regular paths are normalized in the Win32 APIs thus can't address files in the non-normalized form.
|
||||
// This function creates a regular path form but preserves the non-normalized parts of the input (for testing)
|
||||
auto CreateRegularPath = [](PCWSTR root, PCWSTR name)
|
||||
{
|
||||
wil::unique_hlocal_string path;
|
||||
THROW_IF_FAILED(PathAllocCombine(root, name, PATHCCH_DO_NOT_NORMALIZE_SEGMENTS, &path));
|
||||
REQUIRE_FALSE(wil::is_extended_length_path(path.get()));
|
||||
return path;
|
||||
};
|
||||
|
||||
struct TestCases
|
||||
{
|
||||
PCWSTR CreateWithName;
|
||||
PCWSTR DeleteWithName;
|
||||
wil::unique_hlocal_string (*CreatePathFunction)(PCWSTR root, PCWSTR name);
|
||||
HRESULT ExpectedResult;
|
||||
};
|
||||
|
||||
PCWSTR NormalizedName = L"Foo";
|
||||
PCWSTR NonNormalizedName = L"Foo."; // The dot at the end is what makes this non-normalized.
|
||||
const auto PathNotFoundError = HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND);
|
||||
|
||||
TestCases tests[] =
|
||||
{
|
||||
{ NormalizedName, NormalizedName, CreateRegularPath, S_OK },
|
||||
{ NonNormalizedName, NormalizedName, CreateRegularPath, PathNotFoundError },
|
||||
{ NormalizedName, NonNormalizedName, CreateRegularPath, S_OK },
|
||||
{ NonNormalizedName, NonNormalizedName, CreateRegularPath, PathNotFoundError },
|
||||
{ NormalizedName, NormalizedName, CreatePathThatCanAccessNonNormalizedNames, S_OK },
|
||||
{ NonNormalizedName, NormalizedName, CreatePathThatCanAccessNonNormalizedNames, PathNotFoundError },
|
||||
{ NormalizedName, NonNormalizedName, CreatePathThatCanAccessNonNormalizedNames, PathNotFoundError },
|
||||
{ NonNormalizedName, NonNormalizedName, CreatePathThatCanAccessNonNormalizedNames, S_OK },
|
||||
};
|
||||
|
||||
auto folderRoot = wil::ExpandEnvironmentStringsW(LR"(%TEMP%)");
|
||||
REQUIRE_FALSE(wil::is_extended_length_path(folderRoot.get()));
|
||||
|
||||
auto EnsureFolderWithNonCanonicalNameAndContentsExists = [&](const TestCases& test)
|
||||
{
|
||||
const auto enableNonNormalized = PATHCCH_ENSURE_IS_EXTENDED_LENGTH_PATH | PATHCCH_DO_NOT_NORMALIZE_SEGMENTS;
|
||||
|
||||
wil::unique_hlocal_string targetFolder;
|
||||
// Create a folder for testing using the extended length form to enable
|
||||
// access to non-normalized forms of the path
|
||||
THROW_IF_FAILED(PathAllocCombine(folderRoot.get(), test.CreateWithName, enableNonNormalized, &targetFolder));
|
||||
|
||||
// This ensures the folder is there and won't fail if it already exists (common when testing).
|
||||
wil::CreateDirectoryDeep(targetFolder.get());
|
||||
|
||||
// Create a file in that folder with a non-normalized name (with the dot at the end).
|
||||
wil::unique_hlocal_string extendedFilePath;
|
||||
THROW_IF_FAILED(PathAllocCombine(targetFolder.get(), L"NonNormalized.", enableNonNormalized, &extendedFilePath));
|
||||
wil::unique_hfile fileHandle(CreateFileW(extendedFilePath.get(), FILE_WRITE_ATTRIBUTES,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
|
||||
THROW_LAST_ERROR_IF(!fileHandle);
|
||||
};
|
||||
|
||||
for (auto const& test : tests)
|
||||
{
|
||||
// remove remnants from previous test that will cause failures
|
||||
wil::RemoveDirectoryRecursiveNoThrow(CreatePathThatCanAccessNonNormalizedNames(folderRoot.get(), NormalizedName).get());
|
||||
wil::RemoveDirectoryRecursiveNoThrow(CreatePathThatCanAccessNonNormalizedNames(folderRoot.get(), NonNormalizedName).get());
|
||||
|
||||
EnsureFolderWithNonCanonicalNameAndContentsExists(test);
|
||||
auto deleteWithPath = test.CreatePathFunction(folderRoot.get(), test.DeleteWithName);
|
||||
|
||||
const auto hr = wil::RemoveDirectoryRecursiveNoThrow(deleteWithPath.get());
|
||||
REQUIRE(test.ExpectedResult == hr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// real paths to test
|
||||
const wchar_t c_variablePath[] = L"%systemdrive%\\Windows\\System32\\Windows.Storage.dll";
|
||||
const wchar_t c_expandedPath[] = L"c:\\Windows\\System32\\Windows.Storage.dll";
|
||||
|
||||
// // paths that should not exist on the system
|
||||
const wchar_t c_missingVariable[] = L"%doesnotexist%\\doesnotexist.dll";
|
||||
const wchar_t c_missingPath[] = L"c:\\Windows\\System32\\doesnotexist.dll";
|
||||
|
||||
const int c_stackBufferLimitTest = 5;
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
TEST_CASE("FileSystemTests::VerifyGetCurrentDirectory", "[filesystem]")
|
||||
{
|
||||
auto pwd = wil::GetCurrentDirectoryW();
|
||||
REQUIRE(*pwd.get() != L'\0');
|
||||
}
|
||||
|
||||
TEST_CASE("FileSystemTests::VerifyGetFullPathName", "[filesystem]")
|
||||
{
|
||||
PCWSTR fileName = L"ReadMe.txt";
|
||||
auto result = wil::GetFullPathNameW<wil::unique_cotaskmem_string>(fileName, nullptr);
|
||||
|
||||
PCWSTR fileNameResult;
|
||||
result = wil::GetFullPathNameW<wil::unique_cotaskmem_string>(fileName, &fileNameResult);
|
||||
REQUIRE(wcscmp(fileName, fileNameResult) == 0);
|
||||
auto result2 = wil::GetFullPathNameW<wil::unique_cotaskmem_string, c_stackBufferLimitTest>(fileName, &fileNameResult);
|
||||
REQUIRE(wcscmp(fileName, fileNameResult) == 0);
|
||||
REQUIRE(wcscmp(result.get(), result2.get()) == 0);
|
||||
|
||||
// The only negative test case I've found is a path > 32k.
|
||||
std::wstring big(1024 * 32, L'a');
|
||||
wil::unique_hstring output;
|
||||
auto hr = wil::GetFullPathNameW(big.c_str(), output, nullptr);
|
||||
REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE));
|
||||
}
|
||||
|
||||
TEST_CASE("FileSystemTests::VerifyGetFinalPathNameByHandle", "[filesystem]")
|
||||
{
|
||||
wil::unique_hfile fileHandle(CreateFileW(c_expandedPath, FILE_READ_ATTRIBUTES,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, nullptr));
|
||||
THROW_LAST_ERROR_IF(!fileHandle);
|
||||
|
||||
auto name = wil::GetFinalPathNameByHandleW(fileHandle.get());
|
||||
auto name2 = wil::GetFinalPathNameByHandleW<wil::unique_cotaskmem_string, c_stackBufferLimitTest>(fileHandle.get());
|
||||
REQUIRE(wcscmp(name.get(), name2.get()) == 0);
|
||||
|
||||
std::wstring path;
|
||||
auto hr = wil::GetFinalPathNameByHandleW(nullptr, path);
|
||||
REQUIRE(hr == E_HANDLE); // should be a usage error so be a fail fast.
|
||||
// A more legitimate case is a non file handler like a drive volume.
|
||||
|
||||
wil::unique_hfile volumeHandle(CreateFileW(LR"(\\?\C:)", FILE_READ_ATTRIBUTES,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS, nullptr));
|
||||
THROW_LAST_ERROR_IF(!volumeHandle);
|
||||
const auto hr2 = wil::GetFinalPathNameByHandleW(volumeHandle.get(), path);
|
||||
REQUIRE(hr2 == HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION));
|
||||
}
|
||||
|
||||
TEST_CASE("FileSystemTests::VerifyTrySearchPathW", "[filesystem]")
|
||||
{
|
||||
auto pathToTest = wil::TrySearchPathW(nullptr, c_expandedPath, nullptr);
|
||||
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
|
||||
|
||||
pathToTest = wil::TrySearchPathW(nullptr, c_missingPath, nullptr);
|
||||
REQUIRE(wil::string_get_not_null(pathToTest)[0] == L'\0');
|
||||
}
|
||||
#endif
|
||||
|
||||
// Simple test to expand an environmental string
|
||||
TEST_CASE("FileSystemTests::VerifyExpandEnvironmentStringsW", "[filesystem]")
|
||||
{
|
||||
wil::unique_cotaskmem_string pathToTest;
|
||||
REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(c_variablePath, pathToTest));
|
||||
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
|
||||
|
||||
// This should effectively be a no-op
|
||||
REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(c_expandedPath, pathToTest));
|
||||
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
|
||||
|
||||
// Environment variable does not exist, but the call should still succeed
|
||||
REQUIRE_SUCCEEDED(wil::ExpandEnvironmentStringsW(c_missingVariable, pathToTest));
|
||||
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_missingVariable, -1, TRUE) == CSTR_EQUAL);
|
||||
}
|
||||
|
||||
TEST_CASE("FileSystemTests::VerifySearchPathW", "[filesystem]")
|
||||
{
|
||||
wil::unique_cotaskmem_string pathToTest;
|
||||
REQUIRE_SUCCEEDED(wil::SearchPathW(nullptr, c_expandedPath, nullptr, pathToTest));
|
||||
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
|
||||
|
||||
REQUIRE(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == wil::SearchPathW(nullptr, c_missingPath, nullptr, pathToTest));
|
||||
}
|
||||
|
||||
TEST_CASE("FileSystemTests::VerifyExpandEnvAndSearchPath", "[filesystem]")
|
||||
{
|
||||
wil::unique_cotaskmem_string pathToTest;
|
||||
REQUIRE_SUCCEEDED(wil::ExpandEnvAndSearchPath(c_variablePath, pathToTest));
|
||||
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
|
||||
|
||||
// This test will exercise the case where AdaptFixedSizeToAllocatedResult will need to
|
||||
// reallocate the initial buffer to fit the final string.
|
||||
// This test is sufficient to test both wil::ExpandEnvironmentStringsW and wil::SeachPathW
|
||||
REQUIRE_SUCCEEDED((wil::ExpandEnvAndSearchPath<wil::unique_cotaskmem_string, c_stackBufferLimitTest>(c_variablePath, pathToTest)));
|
||||
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, c_expandedPath, -1, TRUE) == CSTR_EQUAL);
|
||||
|
||||
pathToTest.reset();
|
||||
REQUIRE(HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == wil::ExpandEnvAndSearchPath(c_missingVariable, pathToTest));
|
||||
REQUIRE(pathToTest.get() == nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("FileSystemTests::VerifyGetSystemDirectoryW", "[filesystem]")
|
||||
{
|
||||
wil::unique_cotaskmem_string pathToTest;
|
||||
REQUIRE_SUCCEEDED(wil::GetSystemDirectoryW(pathToTest));
|
||||
|
||||
// allocate based on the string that wil::GetSystemDirectoryW returned
|
||||
size_t length = wcslen(pathToTest.get()) + 1;
|
||||
auto trueSystemDir = wil::make_cotaskmem_string_nothrow(nullptr, length);
|
||||
REQUIRE(GetSystemDirectoryW(trueSystemDir.get(), static_cast<UINT>(length)) > 0);
|
||||
|
||||
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, trueSystemDir.get(), -1, TRUE) == CSTR_EQUAL);
|
||||
|
||||
// Force AdaptFixed* to realloc. Test stack boundary with small initial buffer limit, c_stackBufferLimitTest
|
||||
REQUIRE_SUCCEEDED((wil::GetSystemDirectoryW<wil::unique_cotaskmem_string, c_stackBufferLimitTest>(pathToTest)));
|
||||
|
||||
// allocate based on the string that wil::GetSystemDirectoryW returned
|
||||
length = wcslen(pathToTest.get()) + 1;
|
||||
trueSystemDir = wil::make_cotaskmem_string_nothrow(nullptr, length);
|
||||
REQUIRE(GetSystemDirectoryW(trueSystemDir.get(), static_cast<UINT>(length)) > 0);
|
||||
|
||||
REQUIRE(CompareStringOrdinal(pathToTest.get(), -1, trueSystemDir.get(), -1, TRUE) == CSTR_EQUAL);
|
||||
}
|
||||
|
||||
struct has_operator_pcwstr
|
||||
{
|
||||
PCWSTR value;
|
||||
operator PCWSTR() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
struct has_operator_pwstr
|
||||
{
|
||||
PWSTR value;
|
||||
operator PWSTR() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
struct has_operator_wstr
|
||||
{
|
||||
std::wstring value;
|
||||
operator const std::wstring&() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
TEST_CASE("FileSystemTests::VerifyStrConcat", "[filesystem]")
|
||||
{
|
||||
SECTION("Concat with multiple strings")
|
||||
{
|
||||
PCWSTR test1 = L"Test1";
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
std::wstring test2 = L"Test2";
|
||||
#else
|
||||
PCWSTR test2 = L"Test2";
|
||||
#endif
|
||||
WCHAR test3[6] = L"Test3";
|
||||
wil::unique_cotaskmem_string test4 = wil::make_unique_string_nothrow<wil::unique_cotaskmem_string>(L"test4");
|
||||
wil::unique_hstring test5 = wil::make_unique_string_nothrow<wil::unique_hstring>(L"test5");
|
||||
|
||||
has_operator_pcwstr test6{ L"Test6" };
|
||||
WCHAR test7Buffer[] = L"Test7";
|
||||
has_operator_pwstr test7{ test7Buffer };
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
has_operator_wstr test8{ L"Test8" };
|
||||
#else
|
||||
PCWSTR test8 = L"Test8";
|
||||
#endif
|
||||
PCWSTR expectedStr = L"Test1Test2Test3Test4Test5Test6Test7Test8";
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
auto combinedString = wil::str_concat<wil::unique_cotaskmem_string>(test1, test2, test3, test4, test5, test6, test7, test8);
|
||||
REQUIRE(CompareStringOrdinal(combinedString.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL);
|
||||
#endif
|
||||
|
||||
wil::unique_cotaskmem_string combinedStringNT;
|
||||
REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, test1, test2, test3, test4, test5, test6, test7, test8));
|
||||
REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL);
|
||||
|
||||
auto combinedStringFF = wil::str_concat_failfast<wil::unique_cotaskmem_string>(test1, test2, test3, test4, test5, test6, test7, test8);
|
||||
REQUIRE(CompareStringOrdinal(combinedStringFF.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL);
|
||||
}
|
||||
|
||||
SECTION("Concat with single string")
|
||||
{
|
||||
PCWSTR test1 = L"Test1";
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
auto combinedString = wil::str_concat<wil::unique_cotaskmem_string>(test1);
|
||||
REQUIRE(CompareStringOrdinal(combinedString.get(), -1, test1, -1, TRUE) == CSTR_EQUAL);
|
||||
#endif
|
||||
|
||||
wil::unique_cotaskmem_string combinedStringNT;
|
||||
REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, test1));
|
||||
REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, test1, -1, TRUE) == CSTR_EQUAL);
|
||||
|
||||
auto combinedStringFF = wil::str_concat_failfast<wil::unique_cotaskmem_string>(test1);
|
||||
REQUIRE(CompareStringOrdinal(combinedStringFF.get(), -1, test1, -1, TRUE) == CSTR_EQUAL);
|
||||
}
|
||||
|
||||
SECTION("Concat with existing string")
|
||||
{
|
||||
std::wstring test2 = L"Test2";
|
||||
WCHAR test3[6] = L"Test3";
|
||||
PCWSTR expectedStr = L"Test1Test2Test3";
|
||||
|
||||
wil::unique_cotaskmem_string combinedStringNT = wil::make_unique_string_nothrow<wil::unique_cotaskmem_string>(L"Test1");
|
||||
REQUIRE_SUCCEEDED(wil::str_concat_nothrow(combinedStringNT, test2.c_str(), test3));
|
||||
REQUIRE(CompareStringOrdinal(combinedStringNT.get(), -1, expectedStr, -1, TRUE) == CSTR_EQUAL);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("FileSystemTests::VerifyStrPrintf", "[filesystem]")
|
||||
{
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
auto formattedString = wil::str_printf<wil::unique_cotaskmem_string>(L"Test %s %c %d %4.2f", L"String", L'c', 42, 6.28);
|
||||
REQUIRE(CompareStringOrdinal(formattedString.get(), -1, L"Test String c 42 6.28", -1, TRUE) == CSTR_EQUAL);
|
||||
#endif
|
||||
|
||||
wil::unique_cotaskmem_string formattedStringNT;
|
||||
REQUIRE_SUCCEEDED(wil::str_printf_nothrow(formattedStringNT, L"Test %s %c %d %4.2f", L"String", L'c', 42, 6.28));
|
||||
REQUIRE(CompareStringOrdinal(formattedStringNT.get(), -1, L"Test String c 42 6.28", -1, TRUE) == CSTR_EQUAL);
|
||||
|
||||
auto formattedStringFF = wil::str_printf_failfast<wil::unique_cotaskmem_string>(L"Test %s %c %d %4.2f", L"String", L'c', 42, 6.28);
|
||||
REQUIRE(CompareStringOrdinal(formattedStringFF.get(), -1, L"Test String c 42 6.28", -1, TRUE) == CSTR_EQUAL);
|
||||
}
|
||||
|
||||
TEST_CASE("FileSystemTests::VerifyGetModuleFileNameW", "[filesystem]")
|
||||
{
|
||||
wil::unique_cotaskmem_string path;
|
||||
REQUIRE_SUCCEEDED(wil::GetModuleFileNameW(nullptr, path));
|
||||
auto len = wcslen(path.get());
|
||||
REQUIRE(((len >= 4) && (wcscmp(path.get() + len - 4, L".exe") == 0)));
|
||||
|
||||
// Call again, but force multiple retries through a small initial buffer
|
||||
wil::unique_cotaskmem_string path2;
|
||||
REQUIRE_SUCCEEDED((wil::GetModuleFileNameW<wil::unique_cotaskmem_string, 4>(nullptr, path2)));
|
||||
REQUIRE(wcscmp(path.get(), path2.get()) == 0);
|
||||
|
||||
REQUIRE_FAILED(wil::GetModuleFileNameW((HMODULE)INVALID_HANDLE_VALUE, path));
|
||||
}
|
||||
|
||||
TEST_CASE("FileSystemTests::VerifyGetModuleFileNameExW", "[filesystem]")
|
||||
{
|
||||
wil::unique_cotaskmem_string path;
|
||||
REQUIRE_SUCCEEDED(wil::GetModuleFileNameExW(nullptr, nullptr, path));
|
||||
auto len = wcslen(path.get());
|
||||
REQUIRE(((len >= 4) && (wcscmp(path.get() + len - 4, L".exe") == 0)));
|
||||
|
||||
// Call again, but force multiple retries through a small initial buffer
|
||||
wil::unique_cotaskmem_string path2;
|
||||
REQUIRE_SUCCEEDED((wil::GetModuleFileNameExW<wil::unique_cotaskmem_string, 4>(nullptr, nullptr, path2)));
|
||||
REQUIRE(wcscmp(path.get(), path2.get()) == 0);
|
||||
|
||||
REQUIRE_FAILED(wil::GetModuleFileNameExW(nullptr, (HMODULE)INVALID_HANDLE_VALUE, path));
|
||||
}
|
||||
|
||||
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
|
@ -0,0 +1,145 @@
|
|||
#pragma once
|
||||
|
||||
#include "catch.hpp"
|
||||
#include <objbase.h>
|
||||
#include <wil/wistd_functional.h>
|
||||
#include <wrl/implements.h>
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
|
||||
|
||||
// IMallocSpy requires you to implement all methods, but we often only want one or two...
|
||||
struct MallocSpy : Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IMallocSpy>
|
||||
{
|
||||
wistd::function<SIZE_T(SIZE_T)> PreAllocCallback;
|
||||
virtual SIZE_T STDMETHODCALLTYPE PreAlloc(SIZE_T requestSize) override
|
||||
{
|
||||
if (PreAllocCallback)
|
||||
{
|
||||
return PreAllocCallback(requestSize);
|
||||
}
|
||||
|
||||
return requestSize;
|
||||
}
|
||||
|
||||
wistd::function<void*(void*)> PostAllocCallback;
|
||||
virtual void* STDMETHODCALLTYPE PostAlloc(void* ptr) override
|
||||
{
|
||||
if (PostAllocCallback)
|
||||
{
|
||||
return PostAllocCallback(ptr);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
wistd::function<void*(void*)> PreFreeCallback;
|
||||
virtual void* STDMETHODCALLTYPE PreFree(void* ptr, BOOL wasSpyed) override
|
||||
{
|
||||
if (wasSpyed && PreFreeCallback)
|
||||
{
|
||||
return PreFreeCallback(ptr);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
virtual void STDMETHODCALLTYPE PostFree(BOOL /*wasSpyed*/) override
|
||||
{
|
||||
}
|
||||
|
||||
wistd::function<SIZE_T(void*, SIZE_T, void**)> PreReallocCallback;
|
||||
virtual SIZE_T STDMETHODCALLTYPE PreRealloc(void* ptr, SIZE_T requestSize, void** newPtr, BOOL wasSpyed) override
|
||||
{
|
||||
*newPtr = ptr;
|
||||
if (wasSpyed && PreReallocCallback)
|
||||
{
|
||||
return PreReallocCallback(ptr, requestSize, newPtr);
|
||||
}
|
||||
|
||||
return requestSize;
|
||||
}
|
||||
|
||||
wistd::function<void*(void*)> PostReallocCallback;
|
||||
virtual void* STDMETHODCALLTYPE PostRealloc(void* ptr, BOOL wasSpyed) override
|
||||
{
|
||||
if (wasSpyed && PostReallocCallback)
|
||||
{
|
||||
return PostReallocCallback(ptr);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
wistd::function<void*(void*)> PreGetSizeCallback;
|
||||
virtual void* STDMETHODCALLTYPE PreGetSize(void* ptr, BOOL wasSpyed) override
|
||||
{
|
||||
if (wasSpyed && PreGetSizeCallback)
|
||||
{
|
||||
return PreGetSizeCallback(ptr);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
wistd::function<SIZE_T(SIZE_T)> PostGetSizeCallback;
|
||||
virtual SIZE_T STDMETHODCALLTYPE PostGetSize(SIZE_T size, BOOL wasSpyed) override
|
||||
{
|
||||
if (wasSpyed && PostGetSizeCallback)
|
||||
{
|
||||
return PostGetSizeCallback(size);
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
wistd::function<void*(void*)> PreDidAllocCallback;
|
||||
virtual void* STDMETHODCALLTYPE PreDidAlloc(void* ptr, BOOL wasSpyed) override
|
||||
{
|
||||
if (wasSpyed && PreDidAllocCallback)
|
||||
{
|
||||
return PreDidAllocCallback(ptr);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
virtual int STDMETHODCALLTYPE PostDidAlloc(void* /*ptr*/, BOOL /*wasSpyed*/, int result) override
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
virtual void STDMETHODCALLTYPE PreHeapMinimize() override
|
||||
{
|
||||
}
|
||||
|
||||
virtual void STDMETHODCALLTYPE PostHeapMinimize() override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
Microsoft::WRL::ComPtr<MallocSpy> MakeSecureDeleterMallocSpy()
|
||||
{
|
||||
using namespace Microsoft::WRL;
|
||||
auto result = Make<MallocSpy>();
|
||||
REQUIRE(result);
|
||||
|
||||
result->PreFreeCallback = [](void* ptr)
|
||||
{
|
||||
ComPtr<IMalloc> malloc;
|
||||
if (SUCCEEDED(::CoGetMalloc(1, &malloc)))
|
||||
{
|
||||
auto size = malloc->GetSize(ptr);
|
||||
auto buffer = static_cast<byte*>(ptr);
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
REQUIRE(buffer[i] == 0);
|
||||
}
|
||||
}
|
||||
|
||||
return ptr;
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,598 @@
|
|||
|
||||
// Included first and then again later to ensure that we're able to "light up" new functionality based off new includes
|
||||
#include <wil/resource.h>
|
||||
|
||||
// Headers to "light up" functionality in resource.h
|
||||
#include <memory>
|
||||
#include <roapi.h>
|
||||
#include <winstring.h>
|
||||
|
||||
#include <wil/com.h>
|
||||
#include <wil/resource.h>
|
||||
#include <wil/stl.h>
|
||||
#include <wrl/implements.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
TEST_CASE("ResourceTests::TestScopeExit", "[resource][scope_exit]")
|
||||
{
|
||||
int count = 0;
|
||||
auto validate = [&](int expected) { REQUIRE(count == expected); count = 0; };
|
||||
|
||||
{
|
||||
auto foo = wil::scope_exit([&] { count++; });
|
||||
}
|
||||
validate(1);
|
||||
|
||||
{
|
||||
auto foo = wil::scope_exit([&] { count++; });
|
||||
foo.release();
|
||||
foo.reset();
|
||||
}
|
||||
validate(0);
|
||||
|
||||
{
|
||||
auto foo = wil::scope_exit([&] { count++; });
|
||||
foo.reset();
|
||||
foo.reset();
|
||||
validate(1);
|
||||
}
|
||||
validate(0);
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
{
|
||||
auto foo = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { count++; THROW_HR(E_FAIL); });
|
||||
}
|
||||
validate(1);
|
||||
|
||||
{
|
||||
auto foo = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { count++; THROW_HR(E_FAIL); });
|
||||
foo.release();
|
||||
foo.reset();
|
||||
}
|
||||
validate(0);
|
||||
|
||||
{
|
||||
auto foo = wil::scope_exit_log(WI_DIAGNOSTICS_INFO, [&] { count++; THROW_HR(E_FAIL); });
|
||||
foo.reset();
|
||||
foo.reset();
|
||||
validate(1);
|
||||
}
|
||||
validate(0);
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
}
|
||||
|
||||
interface __declspec(uuid("ececcc6a-5193-4d14-b38e-ed1460c20b00"))
|
||||
ITest : public IUnknown
|
||||
{
|
||||
STDMETHOD_(void, Test)() = 0;
|
||||
};
|
||||
|
||||
class PointerTestObject : witest::AllocatedObject,
|
||||
public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>, ITest>
|
||||
{
|
||||
public:
|
||||
STDMETHOD_(void, Test)() {};
|
||||
};
|
||||
|
||||
TEST_CASE("ResourceTests::TestOperationsOnGenericSmartPointerClasses", "[resource]")
|
||||
{
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
{
|
||||
// wil::unique_any_t example
|
||||
wil::unique_event ptr2(wil::EventOptions::ManualReset);
|
||||
// wil::com_ptr
|
||||
wil::com_ptr<PointerTestObject> ptr3 = Microsoft::WRL::Make<PointerTestObject>();
|
||||
// wil::shared_any_t example
|
||||
wil::shared_event ptr4(wil::EventOptions::ManualReset);
|
||||
// wistd::unique_ptr example
|
||||
auto ptr5 = wil::make_unique_failfast<POINT>();
|
||||
|
||||
static_assert(wistd::is_same<typename wil::smart_pointer_details<decltype(ptr2)>::pointer, HANDLE>::value, "type-mismatch");
|
||||
static_assert(wistd::is_same<typename wil::smart_pointer_details<decltype(ptr3)>::pointer, PointerTestObject*>::value, "type-mismatch");
|
||||
|
||||
auto p2 = wil::detach_from_smart_pointer(ptr2);
|
||||
auto p3 = wil::detach_from_smart_pointer(ptr3);
|
||||
// auto p4 = wil::detach_from_smart_pointer(ptr4); // wil::shared_any_t and std::shared_ptr do not support release().
|
||||
HANDLE p4{};
|
||||
auto p5 = wil::detach_from_smart_pointer(ptr5);
|
||||
|
||||
REQUIRE((!ptr2 && !ptr3));
|
||||
REQUIRE((p2 && p3));
|
||||
|
||||
wil::attach_to_smart_pointer(ptr2, p2);
|
||||
wil::attach_to_smart_pointer(ptr3, p3);
|
||||
wil::attach_to_smart_pointer(ptr4, p4);
|
||||
wil::attach_to_smart_pointer(ptr5, p5);
|
||||
|
||||
p2 = nullptr;
|
||||
p3 = nullptr;
|
||||
p4 = nullptr;
|
||||
p5 = nullptr;
|
||||
|
||||
wil::detach_to_opt_param(&p2, ptr2);
|
||||
wil::detach_to_opt_param(&p3, ptr3);
|
||||
|
||||
REQUIRE((!ptr2 && !ptr3));
|
||||
REQUIRE((p2 && p3));
|
||||
|
||||
wil::attach_to_smart_pointer(ptr2, p2);
|
||||
wil::attach_to_smart_pointer(ptr3, p3);
|
||||
p2 = nullptr;
|
||||
p3 = nullptr;
|
||||
|
||||
wil::detach_to_opt_param(&p2, ptr2);
|
||||
wil::detach_to_opt_param(&p3, ptr3);
|
||||
REQUIRE((!ptr2 && !ptr3));
|
||||
REQUIRE((p2 && p3));
|
||||
|
||||
[&](decltype(p2)* ptr) { *ptr = p2; } (wil::out_param(ptr2));
|
||||
[&](decltype(p3)* ptr) { *ptr = p3; } (wil::out_param(ptr3));
|
||||
[&](decltype(p4)* ptr) { *ptr = p4; } (wil::out_param(ptr4));
|
||||
[&](decltype(p5)* ptr) { *ptr = p5; } (wil::out_param(ptr5));
|
||||
|
||||
REQUIRE((ptr2 && ptr3));
|
||||
|
||||
// Validate R-Value compilation
|
||||
wil::detach_to_opt_param(&p2, decltype(ptr2){});
|
||||
wil::detach_to_opt_param(&p3, decltype(ptr3){});
|
||||
}
|
||||
#endif
|
||||
|
||||
std::unique_ptr<int> ptr1(new int(1));
|
||||
Microsoft::WRL::ComPtr<PointerTestObject> ptr4 = Microsoft::WRL::Make<PointerTestObject>();
|
||||
|
||||
static_assert(wistd::is_same<typename wil::smart_pointer_details<decltype(ptr1)>::pointer, int*>::value, "type-mismatch");
|
||||
static_assert(wistd::is_same<typename wil::smart_pointer_details<decltype(ptr4)>::pointer, PointerTestObject*>::value, "type-mismatch");
|
||||
|
||||
auto p1 = wil::detach_from_smart_pointer(ptr1);
|
||||
auto p4 = wil::detach_from_smart_pointer(ptr4);
|
||||
|
||||
REQUIRE((!ptr1 && !ptr4));
|
||||
REQUIRE((p1 && p4));
|
||||
|
||||
wil::attach_to_smart_pointer(ptr1, p1);
|
||||
wil::attach_to_smart_pointer(ptr4, p4);
|
||||
|
||||
REQUIRE((ptr1 && ptr4));
|
||||
|
||||
p1 = nullptr;
|
||||
p4 = nullptr;
|
||||
|
||||
int** pNull = nullptr;
|
||||
wil::detach_to_opt_param(pNull, ptr1);
|
||||
REQUIRE(ptr1);
|
||||
|
||||
wil::detach_to_opt_param(&p1, ptr1);
|
||||
wil::detach_to_opt_param(&p4, ptr4);
|
||||
|
||||
REQUIRE((!ptr1 && !ptr4));
|
||||
REQUIRE((p1 && p4));
|
||||
|
||||
[&](decltype(p1)* ptr) { *ptr = p1; } (wil::out_param(ptr1));
|
||||
[&](decltype(p4)* ptr) { *ptr = p4; } (wil::out_param(ptr4));
|
||||
|
||||
REQUIRE((ptr1 && ptr4));
|
||||
|
||||
p1 = wil::detach_from_smart_pointer(ptr1);
|
||||
[&](int** ptr) { *ptr = p1; } (wil::out_param_ptr<int **>(ptr1));
|
||||
REQUIRE(ptr1);
|
||||
}
|
||||
|
||||
// Compilation only test...
|
||||
void StlAdlTest()
|
||||
{
|
||||
// This test has exposed some Argument Dependent Lookup issues in wistd / stl. Primarily we're
|
||||
// just looking for clean compilation.
|
||||
|
||||
std::vector<wistd::unique_ptr<int>> v;
|
||||
v.emplace_back(new int{ 1 });
|
||||
v.emplace_back(new int{ 2 });
|
||||
v.emplace_back(new int{ 3 });
|
||||
std::rotate(begin(v), begin(v) + 1, end(v));
|
||||
|
||||
REQUIRE(*v[0] == 1);
|
||||
REQUIRE(*v[1] == 3);
|
||||
REQUIRE(*v[2] == 2);
|
||||
|
||||
decltype(v) v2;
|
||||
v2 = std::move(v);
|
||||
REQUIRE(*v2[0] == 1);
|
||||
REQUIRE(*v2[1] == 3);
|
||||
REQUIRE(*v2[2] == 2);
|
||||
|
||||
decltype(v) v3;
|
||||
std::swap(v2, v3);
|
||||
REQUIRE(*v3[0] == 1);
|
||||
REQUIRE(*v3[1] == 3);
|
||||
REQUIRE(*v3[2] == 2);
|
||||
}
|
||||
|
||||
// Compilation only test...
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
void UniqueProcessInfo()
|
||||
{
|
||||
wil::unique_process_information process;
|
||||
CreateProcessW(nullptr, nullptr, nullptr, nullptr, FALSE, 0, nullptr, nullptr, nullptr, &process);
|
||||
ResumeThread(process.hThread);
|
||||
WaitForSingleObject(process.hProcess, INFINITE);
|
||||
wil::unique_process_information other(wistd::move(process));
|
||||
}
|
||||
#endif
|
||||
|
||||
struct FakeComInterface
|
||||
{
|
||||
void AddRef()
|
||||
{
|
||||
refs++;
|
||||
}
|
||||
void Release()
|
||||
{
|
||||
refs--;
|
||||
}
|
||||
|
||||
HRESULT __stdcall Close()
|
||||
{
|
||||
closes++;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
size_t refs = 0;
|
||||
size_t closes = 0;
|
||||
|
||||
bool called()
|
||||
{
|
||||
auto old = closes;
|
||||
closes = 0;
|
||||
return (old > 0);
|
||||
}
|
||||
|
||||
bool has_ref()
|
||||
{
|
||||
return (refs > 0);
|
||||
}
|
||||
};
|
||||
|
||||
static void __stdcall CloseFakeComInterface(FakeComInterface* fake)
|
||||
{
|
||||
fake->Close();
|
||||
}
|
||||
|
||||
using unique_fakeclose_call = wil::unique_com_call<FakeComInterface, decltype(&CloseFakeComInterface), CloseFakeComInterface>;
|
||||
|
||||
TEST_CASE("ResourceTests::VerifyUniqueComCall", "[resource][unique_com_call]")
|
||||
{
|
||||
unique_fakeclose_call call1;
|
||||
unique_fakeclose_call call2;
|
||||
|
||||
// intentional compilation errors
|
||||
// unique_fakeclose_call call3 = call1;
|
||||
// call2 = call1;
|
||||
|
||||
FakeComInterface fake1;
|
||||
unique_fakeclose_call call4(&fake1);
|
||||
REQUIRE(fake1.has_ref());
|
||||
|
||||
unique_fakeclose_call call5(wistd::move(call4));
|
||||
REQUIRE(!call4);
|
||||
REQUIRE(call5);
|
||||
REQUIRE(fake1.has_ref());
|
||||
|
||||
call4 = wistd::move(call5);
|
||||
REQUIRE(call4);
|
||||
REQUIRE(!call5);
|
||||
REQUIRE(fake1.has_ref());
|
||||
REQUIRE(!fake1.called());
|
||||
|
||||
FakeComInterface fake2;
|
||||
{
|
||||
unique_fakeclose_call scoped(&fake2);
|
||||
}
|
||||
REQUIRE(!fake2.has_ref());
|
||||
REQUIRE(fake2.called());
|
||||
|
||||
call4.reset(&fake2);
|
||||
REQUIRE(fake1.called());
|
||||
REQUIRE(!fake1.has_ref());
|
||||
call4.reset();
|
||||
REQUIRE(!fake2.has_ref());
|
||||
REQUIRE(fake2.called());
|
||||
|
||||
call1.reset(&fake1);
|
||||
call2.swap(call1);
|
||||
REQUIRE((call2 && !call1));
|
||||
|
||||
call2.release();
|
||||
REQUIRE(!fake1.called());
|
||||
REQUIRE(!fake1.has_ref());
|
||||
REQUIRE(!call2);
|
||||
|
||||
REQUIRE(*call1.addressof() == nullptr);
|
||||
|
||||
call1.reset(&fake1);
|
||||
|
||||
fake2.closes = 0;
|
||||
fake2.refs = 1;
|
||||
*(&call1) = &fake2;
|
||||
REQUIRE(!fake1.has_ref());
|
||||
REQUIRE(fake1.called());
|
||||
REQUIRE(fake2.has_ref());
|
||||
call1.reset();
|
||||
REQUIRE(!fake2.has_ref());
|
||||
REQUIRE(fake2.called());
|
||||
}
|
||||
|
||||
static bool g_called = false;
|
||||
static bool called()
|
||||
{
|
||||
auto call = g_called;
|
||||
g_called = false;
|
||||
return (call);
|
||||
}
|
||||
|
||||
static void __stdcall FakeCall()
|
||||
{
|
||||
g_called = true;
|
||||
}
|
||||
|
||||
using unique_fake_call = wil::unique_call<decltype(&FakeCall), FakeCall>;
|
||||
|
||||
TEST_CASE("ResourceTests::VerifyUniqueCall", "[resource][unique_call]")
|
||||
{
|
||||
unique_fake_call call1;
|
||||
unique_fake_call call2;
|
||||
|
||||
// intentional compilation errors
|
||||
// unique_fake_call call3 = call1;
|
||||
// call2 = call1;
|
||||
|
||||
unique_fake_call call4;
|
||||
REQUIRE(!called());
|
||||
|
||||
unique_fake_call call5(wistd::move(call4));
|
||||
REQUIRE(!call4);
|
||||
REQUIRE(call5);
|
||||
|
||||
call4 = wistd::move(call5);
|
||||
REQUIRE(call4);
|
||||
REQUIRE(!call5);
|
||||
REQUIRE(!called());
|
||||
|
||||
{
|
||||
unique_fake_call scoped;
|
||||
}
|
||||
REQUIRE(called());
|
||||
|
||||
call4.reset();
|
||||
REQUIRE(called());
|
||||
call4.reset();
|
||||
REQUIRE(!called());
|
||||
|
||||
call1.release();
|
||||
REQUIRE((!call1 && call2));
|
||||
call2.swap(call1);
|
||||
REQUIRE((call1 && !call2));
|
||||
|
||||
call2.release();
|
||||
REQUIRE(!called());
|
||||
REQUIRE(!call2);
|
||||
|
||||
#ifdef __WIL__ROAPI_H_APPEXCEPTIONAL
|
||||
{
|
||||
auto call = wil::RoInitialize();
|
||||
}
|
||||
#endif
|
||||
#ifdef __WIL__ROAPI_H_APP
|
||||
{
|
||||
wil::unique_rouninitialize_call uninit;
|
||||
uninit.release();
|
||||
|
||||
auto call = wil::RoInitialize_failfast();
|
||||
}
|
||||
#endif
|
||||
#ifdef __WIL__COMBASEAPI_H_APPEXCEPTIONAL
|
||||
{
|
||||
auto call = wil::CoInitializeEx();
|
||||
}
|
||||
#endif
|
||||
#ifdef __WIL__COMBASEAPI_H_APP
|
||||
{
|
||||
wil::unique_couninitialize_call uninit;
|
||||
uninit.release();
|
||||
|
||||
auto call = wil::CoInitializeEx_failfast();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void UniqueCallCompilationTest()
|
||||
{
|
||||
#ifdef __WIL__COMBASEAPI_H_EXCEPTIONAL
|
||||
{
|
||||
auto call = wil::CoImpersonateClient();
|
||||
}
|
||||
#endif
|
||||
#ifdef __WIL__COMBASEAPI_H_
|
||||
{
|
||||
wil::unique_coreverttoself_call uninit;
|
||||
uninit.release();
|
||||
|
||||
auto call = wil::CoImpersonateClient_failfast();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename StringType, typename VerifyContents>
|
||||
static void TestStringMaker(VerifyContents&& verifyContents)
|
||||
{
|
||||
PCWSTR values[] =
|
||||
{
|
||||
L"",
|
||||
L"value",
|
||||
// 300 chars
|
||||
L"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
L"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
L"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
|
||||
};
|
||||
|
||||
for (const auto& value : values)
|
||||
{
|
||||
auto const valueLength = wcslen(value);
|
||||
|
||||
// Direct construction case.
|
||||
wil::details::string_maker<StringType> maker;
|
||||
THROW_IF_FAILED(maker.make(value, valueLength));
|
||||
auto result = maker.release();
|
||||
verifyContents(value, valueLength, result);
|
||||
|
||||
// Two phase construction case.
|
||||
THROW_IF_FAILED(maker.make(nullptr, valueLength));
|
||||
REQUIRE(maker.buffer() != nullptr);
|
||||
// In the case of the wil::unique_hstring and the empty string the buffer is in a read only
|
||||
// section and can't be written to, so StringCchCopy(maker.buffer(), valueLength + 1, value) will fault adding the nul terminator.
|
||||
// Use memcpy_s specifying exact size that will be zero in this case instead.
|
||||
memcpy_s(maker.buffer(), valueLength * sizeof(*value), value, valueLength * sizeof(*value));
|
||||
result = maker.release();
|
||||
verifyContents(value, valueLength, result);
|
||||
|
||||
{
|
||||
// no promote, ensure no leaks (not tested here, inspect in the debugger)
|
||||
wil::details::string_maker<StringType> maker2;
|
||||
THROW_IF_FAILED(maker2.make(value, valueLength));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
template <typename StringType>
|
||||
static void VerifyMakeUniqueString(bool nullValueSupported = true)
|
||||
{
|
||||
if (nullValueSupported)
|
||||
{
|
||||
auto value0 = wil::make_unique_string<StringType>(nullptr, 5);
|
||||
}
|
||||
|
||||
struct
|
||||
{
|
||||
PCWSTR expectedValue;
|
||||
PCWSTR testValue;
|
||||
// this is an optional parameter
|
||||
size_t testLength = static_cast<size_t>(-1);
|
||||
}
|
||||
const testCaseEntries[] =
|
||||
{
|
||||
{ L"value", L"value", 5 },
|
||||
{ L"value", L"value" },
|
||||
{ L"va", L"va\0ue", 5 },
|
||||
{ L"v", L"value", 1 },
|
||||
{ L"\0", L"", 5 },
|
||||
{ L"\0", nullptr, 5 },
|
||||
};
|
||||
|
||||
using maker = wil::details::string_maker<StringType>;
|
||||
for (auto const &entry : testCaseEntries)
|
||||
{
|
||||
bool shouldSkipNullString = ((wcscmp(entry.expectedValue, L"\0") == 0) && !nullValueSupported);
|
||||
if (!shouldSkipNullString)
|
||||
{
|
||||
auto desiredValue = wil::make_unique_string<StringType>(entry.expectedValue);
|
||||
auto stringValue = wil::make_unique_string<StringType>(entry.testValue, entry.testLength);
|
||||
auto stringValueNoThrow = wil::make_unique_string_nothrow<StringType>(entry.testValue, entry.testLength);
|
||||
auto stringValueFailFast = wil::make_unique_string_failfast<StringType>(entry.testValue, entry.testLength);
|
||||
REQUIRE(wcscmp(maker::get(desiredValue), maker::get(stringValue)) == 0);
|
||||
REQUIRE(wcscmp(maker::get(desiredValue), maker::get(stringValueNoThrow)) == 0);
|
||||
REQUIRE(wcscmp(maker::get(desiredValue), maker::get(stringValueFailFast)) == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerCoTaskMem", "[resource][string_maker]")
|
||||
{
|
||||
VerifyMakeUniqueString<wil::unique_cotaskmem_string>();
|
||||
TestStringMaker<wil::unique_cotaskmem_string>(
|
||||
[](PCWSTR value, size_t /*valueLength*/, const wil::unique_cotaskmem_string& result)
|
||||
{
|
||||
REQUIRE(wcscmp(value, result.get()) == 0);
|
||||
});
|
||||
}
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerLocalAlloc", "[resource][string_maker]")
|
||||
{
|
||||
VerifyMakeUniqueString<wil::unique_hlocal_string>();
|
||||
TestStringMaker<wil::unique_hlocal_string>(
|
||||
[](PCWSTR value, size_t /*valueLength*/, const wil::unique_hlocal_string& result)
|
||||
{
|
||||
REQUIRE(wcscmp(value, result.get()) == 0);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerGlobalAlloc", "[resource][string_maker]")
|
||||
{
|
||||
VerifyMakeUniqueString<wil::unique_hglobal_string>();
|
||||
TestStringMaker<wil::unique_hglobal_string>(
|
||||
[](PCWSTR value, size_t /*valueLength*/, const wil::unique_hglobal_string& result)
|
||||
{
|
||||
REQUIRE(wcscmp(value, result.get()) == 0);
|
||||
});
|
||||
}
|
||||
|
||||
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerProcessHeap", "[resource][string_maker]")
|
||||
{
|
||||
VerifyMakeUniqueString<wil::unique_process_heap_string>();
|
||||
TestStringMaker<wil::unique_process_heap_string>(
|
||||
[](PCWSTR value, size_t /*valueLength*/, const wil::unique_process_heap_string& result)
|
||||
{
|
||||
REQUIRE(wcscmp(value, result.get()) == 0);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerHString", "[resource][string_maker]")
|
||||
{
|
||||
wil::unique_hstring value;
|
||||
value.reset(static_cast<HSTRING>(nullptr));
|
||||
|
||||
VerifyMakeUniqueString<wil::unique_hstring>(false);
|
||||
|
||||
TestStringMaker<wil::unique_hstring>(
|
||||
[](PCWSTR value, size_t valueLength, const wil::unique_hstring& result)
|
||||
{
|
||||
UINT32 length;
|
||||
REQUIRE(wcscmp(value, WindowsGetStringRawBuffer(result.get(), &length)) == 0);
|
||||
REQUIRE(valueLength == length);
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
TEST_CASE("UniqueStringAndStringMakerTests::VerifyStringMakerStdWString", "[resource][string_maker]")
|
||||
{
|
||||
std::string s;
|
||||
wil::details::string_maker<std::wstring> maker;
|
||||
|
||||
TestStringMaker<std::wstring>(
|
||||
[](PCWSTR value, size_t valueLength, const std::wstring& result)
|
||||
{
|
||||
REQUIRE(wcscmp(value, result.c_str()) == 0);
|
||||
REQUIRE(result == value);
|
||||
REQUIRE(result.size() == valueLength);
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("UniqueStringAndStringMakerTests::VerifyLegacySTringMakers", "[resource][string_maker]")
|
||||
{
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
auto l = wil::make_hlocal_string(L"value");
|
||||
l = wil::make_hlocal_string_nothrow(L"value");
|
||||
l = wil::make_hlocal_string_failfast(L"value");
|
||||
|
||||
auto p = wil::make_process_heap_string(L"value");
|
||||
p = wil::make_process_heap_string_nothrow(L"value");
|
||||
p = wil::make_process_heap_string_failfast(L"value");
|
||||
#endif
|
||||
auto c = wil::make_cotaskmem_string(L"value");
|
||||
c = wil::make_cotaskmem_string_nothrow(L"value");
|
||||
c = wil::make_cotaskmem_string_failfast(L"value");
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,574 @@
|
|||
|
||||
#include <roerrorapi.h>
|
||||
#include <wil/com.h>
|
||||
#include <wil/result.h>
|
||||
#include <wil/result_originate.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
static volatile long objectCount = 0;
|
||||
struct SharedObject
|
||||
{
|
||||
SharedObject()
|
||||
{
|
||||
::InterlockedIncrement(&objectCount);
|
||||
}
|
||||
|
||||
~SharedObject()
|
||||
{
|
||||
::InterlockedDecrement(&objectCount);
|
||||
}
|
||||
|
||||
void ProcessShutdown()
|
||||
{
|
||||
}
|
||||
|
||||
int value;
|
||||
};
|
||||
|
||||
TEST_CASE("ResultTests::SemaphoreValue", "[result]")
|
||||
{
|
||||
auto TestValue = [&](auto start, auto end)
|
||||
{
|
||||
wil::details_abi::SemaphoreValue semaphore;
|
||||
for (auto index = start; index <= end; index++)
|
||||
{
|
||||
semaphore.Destroy();
|
||||
REQUIRE(SUCCEEDED(semaphore.CreateFromValue(L"test", index)));
|
||||
|
||||
auto num1 = index;
|
||||
auto num2 = index;
|
||||
REQUIRE(SUCCEEDED(semaphore.TryGetValue(L"test", &num1)));
|
||||
REQUIRE(SUCCEEDED(semaphore.TryGetValue(L"test", &num2)));
|
||||
REQUIRE(num1 == index);
|
||||
REQUIRE(num2 == index);
|
||||
}
|
||||
};
|
||||
|
||||
// Test 32-bit values (edge cases)
|
||||
TestValue(0u, 10u);
|
||||
TestValue(250u, 260u);
|
||||
TestValue(0x7FFFFFF0u, 0x7FFFFFFFu);
|
||||
|
||||
// Test 64-bit values (edge cases)
|
||||
TestValue(0ull, 10ull);
|
||||
TestValue(250ull, 260ull);
|
||||
TestValue(0x000000007FFFFFF0ull, 0x000000008000000Full);
|
||||
TestValue(0x00000000FFFFFFF0ull, 0x000000010000000Full);
|
||||
TestValue(0x00000000FFFFFFF0ull, 0x000000010000000Full);
|
||||
TestValue(0x3FFFFFFFFFFFFFF0ull, 0x3FFFFFFFFFFFFFFFull);
|
||||
|
||||
// Test pointer values
|
||||
wil::details_abi::SemaphoreValue semaphore;
|
||||
void* address = &semaphore;
|
||||
REQUIRE(SUCCEEDED(semaphore.CreateFromPointer(L"test", address)));
|
||||
void* ptr;
|
||||
REQUIRE(SUCCEEDED(semaphore.TryGetPointer(L"test", &ptr)));
|
||||
REQUIRE(ptr == address);
|
||||
}
|
||||
|
||||
TEST_CASE("ResultTests::ProcessLocalStorage", "[result]")
|
||||
{
|
||||
// Test process local storage memory and ref-counting
|
||||
{
|
||||
wil::details_abi::ProcessLocalStorage<SharedObject> obj1("ver1");
|
||||
wil::details_abi::ProcessLocalStorage<SharedObject> obj2("ver1");
|
||||
|
||||
auto& o1 = *obj1.GetShared();
|
||||
auto& o2 = *obj2.GetShared();
|
||||
|
||||
REQUIRE(o1.value == 0);
|
||||
REQUIRE(o2.value == 0);
|
||||
o1.value = 42;
|
||||
REQUIRE(o2.value == 42);
|
||||
REQUIRE(objectCount == 1);
|
||||
|
||||
wil::details_abi::ProcessLocalStorage<SharedObject> obj3("ver3");
|
||||
auto& o3 = *obj3.GetShared();
|
||||
|
||||
REQUIRE(o3.value == 0);
|
||||
REQUIRE(objectCount == 2);
|
||||
}
|
||||
|
||||
REQUIRE(objectCount == 0);
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4702) // Unreachable code
|
||||
TEST_CASE("ResultTests::ExceptionHandling", "[result]")
|
||||
{
|
||||
witest::TestFailureCache failures;
|
||||
|
||||
SECTION("Test 'what()' implementation on ResultException")
|
||||
{
|
||||
auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false);
|
||||
try
|
||||
{
|
||||
THROW_HR(E_INVALIDARG);
|
||||
FAIL("Expected an exception");
|
||||
}
|
||||
catch (const std::exception& exception)
|
||||
{
|
||||
REQUIRE(failures.size() == 1);
|
||||
REQUIRE(failures[0].hr == E_INVALIDARG);
|
||||
auto what = exception.what();
|
||||
REQUIRE((what && *what));
|
||||
REQUIRE(strstr(what, "Exception") != nullptr);
|
||||
}
|
||||
}
|
||||
failures.clear();
|
||||
|
||||
SECTION("Test messaging from an unhandled std exception")
|
||||
{
|
||||
// #pragma warning(suppress: 28931) // unused assignment -- it IS being used... seems like a tool issue.
|
||||
auto hr = []()
|
||||
{
|
||||
try
|
||||
{
|
||||
throw std::runtime_error("runtime");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
RETURN_CAUGHT_EXCEPTION();
|
||||
}
|
||||
}();
|
||||
REQUIRE(failures.size() == 1);
|
||||
REQUIRE(failures[0].hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
|
||||
REQUIRE(wcsstr(failures[0].pszMessage, L"runtime") != nullptr); // should get the exception what() string...
|
||||
REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
|
||||
}
|
||||
failures.clear();
|
||||
|
||||
SECTION("Test messaging from bad_alloc")
|
||||
{
|
||||
auto hr = []() -> HRESULT
|
||||
{
|
||||
try
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
RETURN_CAUGHT_EXCEPTION();
|
||||
}
|
||||
}();
|
||||
REQUIRE(failures.size() == 1);
|
||||
REQUIRE(failures[0].hr == E_OUTOFMEMORY);
|
||||
REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
|
||||
REQUIRE(hr == E_OUTOFMEMORY);
|
||||
}
|
||||
failures.clear();
|
||||
|
||||
SECTION("Test messaging from a WIL exception")
|
||||
{
|
||||
auto hr = []() -> HRESULT
|
||||
{
|
||||
try
|
||||
{
|
||||
THROW_HR(E_INVALIDARG);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
RETURN_CAUGHT_EXCEPTION();
|
||||
}
|
||||
return S_OK;
|
||||
}();
|
||||
REQUIRE(failures.size() == 2);
|
||||
REQUIRE(failures[0].hr == E_INVALIDARG);
|
||||
REQUIRE(failures[0].pszMessage == nullptr);
|
||||
REQUIRE(failures[1].hr == E_INVALIDARG);
|
||||
REQUIRE(wcsstr(failures[1].pszMessage, L"Exception") != nullptr); // should get the exception debug string...
|
||||
REQUIRE(hr == E_INVALIDARG);
|
||||
}
|
||||
failures.clear();
|
||||
|
||||
SECTION("Fail fast an unknown exception")
|
||||
{
|
||||
REQUIRE(witest::DoesCodeCrash([]()
|
||||
{
|
||||
try
|
||||
{
|
||||
throw E_INVALIDARG; // bad throw... (long)
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
RETURN_CAUGHT_EXCEPTION();
|
||||
}
|
||||
}));
|
||||
}
|
||||
failures.clear();
|
||||
|
||||
SECTION("Log test (returns hr)")
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
try
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
hr = LOG_CAUGHT_EXCEPTION();
|
||||
auto hrDirect = wil::ResultFromCaughtException();
|
||||
REQUIRE(hr == hrDirect);
|
||||
}
|
||||
REQUIRE(failures.size() == 1);
|
||||
REQUIRE(failures[0].hr == E_OUTOFMEMORY);
|
||||
REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
|
||||
REQUIRE(hr == E_OUTOFMEMORY);
|
||||
}
|
||||
failures.clear();
|
||||
|
||||
SECTION("Fail-fast test")
|
||||
{
|
||||
REQUIRE_CRASH([]()
|
||||
{
|
||||
try
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
FAIL_FAST_CAUGHT_EXCEPTION();
|
||||
}
|
||||
}());
|
||||
}
|
||||
failures.clear();
|
||||
|
||||
SECTION("Exception test (different exception type thrown...)")
|
||||
{
|
||||
auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false);
|
||||
size_t line = 0;
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
line = __LINE__; THROW_NORMALIZED_CAUGHT_EXCEPTION();
|
||||
}
|
||||
}
|
||||
catch (const wil::ResultException& exception)
|
||||
{
|
||||
REQUIRE(exception.GetFailureInfo().uLineNumber == line); // should have thrown new, so we should have the rethrow line number
|
||||
REQUIRE(exception.GetErrorCode() == E_OUTOFMEMORY);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
FAIL();
|
||||
}
|
||||
REQUIRE(failures.size() == 1);
|
||||
REQUIRE(failures[0].hr == E_OUTOFMEMORY);
|
||||
REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
|
||||
}
|
||||
failures.clear();
|
||||
|
||||
SECTION("Exception test (rethrow same exception type...)")
|
||||
{
|
||||
auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false);
|
||||
size_t line = 0;
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
line = __LINE__; THROW_HR(E_OUTOFMEMORY);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
THROW_NORMALIZED_CAUGHT_EXCEPTION();
|
||||
}
|
||||
}
|
||||
catch (const wil::ResultException& exception)
|
||||
{
|
||||
REQUIRE(exception.GetFailureInfo().uLineNumber == line); // should have re-thrown the original exception (with the original line number)
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
FAIL();
|
||||
}
|
||||
}
|
||||
failures.clear();
|
||||
|
||||
SECTION("Test catch message")
|
||||
{
|
||||
try
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG_CAUGHT_EXCEPTION_MSG("train: %d", 42);
|
||||
}
|
||||
REQUIRE(failures.size() == 1);
|
||||
REQUIRE(failures[0].hr == E_OUTOFMEMORY);
|
||||
REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
|
||||
REQUIRE(wcsstr(failures[0].pszMessage, L"train") != nullptr); // should *also* get the message...
|
||||
REQUIRE(wcsstr(failures[0].pszMessage, L"42") != nullptr);
|
||||
}
|
||||
failures.clear();
|
||||
|
||||
SECTION("Test messaging from a WIL exception")
|
||||
{
|
||||
auto hr = []() -> HRESULT
|
||||
{
|
||||
try
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
RETURN_CAUGHT_EXCEPTION_EXPECTED();
|
||||
}
|
||||
}();
|
||||
REQUIRE(failures.size() == 0);
|
||||
REQUIRE(hr == E_OUTOFMEMORY);
|
||||
}
|
||||
failures.clear();
|
||||
|
||||
SECTION("Test ResultFromException...")
|
||||
{
|
||||
auto hrOk = wil::ResultFromException([&]
|
||||
{
|
||||
});
|
||||
REQUIRE(hrOk == S_OK);
|
||||
|
||||
auto hr = wil::ResultFromException([&]
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
});
|
||||
REQUIRE(failures.size() == 0);
|
||||
REQUIRE(hr == E_OUTOFMEMORY);
|
||||
}
|
||||
failures.clear();
|
||||
|
||||
SECTION("Explicit failfast for unrecognized")
|
||||
{
|
||||
REQUIRE_CRASH(wil::ResultFromException([&]
|
||||
{
|
||||
throw E_FAIL;
|
||||
}));
|
||||
}
|
||||
failures.clear();
|
||||
|
||||
SECTION("Manual debug-only validation of the SEH failfast")
|
||||
{
|
||||
auto hr1 = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]()
|
||||
{
|
||||
// Uncomment to test SEH fail-fast
|
||||
// throw E_FAIL;
|
||||
});
|
||||
REQUIRE(hr1 == S_OK);
|
||||
|
||||
auto hr2 = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::Thrown, [&]
|
||||
{
|
||||
// Uncomment to test SEH fail-fast
|
||||
// throw std::range_error("range");
|
||||
});
|
||||
REQUIRE(hr2 == S_OK);
|
||||
|
||||
wil::FailFastException(WI_DIAGNOSTICS_INFO, [&]
|
||||
{
|
||||
// Uncomment to test SEH fail-fast
|
||||
// THROW_HR(E_FAIL);
|
||||
});
|
||||
}
|
||||
failures.clear();
|
||||
|
||||
SECTION("Standard")
|
||||
{
|
||||
auto line = __LINE__; auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]
|
||||
{
|
||||
THROW_HR(E_INVALIDARG);
|
||||
});
|
||||
REQUIRE(failures.size() == 2);
|
||||
REQUIRE(static_cast<decltype(line)>(failures[1].uLineNumber) == line);
|
||||
REQUIRE(hr == E_INVALIDARG);
|
||||
}
|
||||
failures.clear();
|
||||
|
||||
SECTION("bad_alloc")
|
||||
{
|
||||
auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]
|
||||
{
|
||||
throw std::bad_alloc();
|
||||
});
|
||||
REQUIRE(failures.size() == 1);
|
||||
REQUIRE(hr == E_OUTOFMEMORY);
|
||||
}
|
||||
failures.clear();
|
||||
|
||||
SECTION("std::exception")
|
||||
{
|
||||
auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]
|
||||
{
|
||||
throw std::range_error("range");
|
||||
});
|
||||
REQUIRE(failures.size() == 1);
|
||||
REQUIRE(wcsstr(failures[0].pszMessage, L"range") != nullptr);
|
||||
REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
|
||||
}
|
||||
}
|
||||
|
||||
void ExceptionHandlingCompilationTest()
|
||||
{
|
||||
[]{ try { throw std::bad_alloc(); } CATCH_RETURN(); }();
|
||||
[]{ try { throw std::bad_alloc(); } CATCH_RETURN_MSG("train: %d", 42); }();
|
||||
[]{ try { throw std::bad_alloc(); } CATCH_RETURN_EXPECTED(); }();
|
||||
[]{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION(); } }();
|
||||
[]{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION_MSG("train: %d", 42); } }();
|
||||
[]{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION_EXPECTED(); } }();
|
||||
|
||||
try { throw std::bad_alloc(); } CATCH_LOG();
|
||||
try { throw std::bad_alloc(); } CATCH_LOG_MSG("train: %d", 42);
|
||||
try { throw std::bad_alloc(); } catch (...) { LOG_CAUGHT_EXCEPTION(); }
|
||||
try { throw std::bad_alloc(); } catch (...) { LOG_CAUGHT_EXCEPTION_MSG("train: %d", 42); }
|
||||
|
||||
try { throw std::bad_alloc(); } CATCH_FAIL_FAST();
|
||||
try { throw std::bad_alloc(); } CATCH_FAIL_FAST_MSG("train: %d", 42);
|
||||
try { throw std::bad_alloc(); } catch (...) { FAIL_FAST_CAUGHT_EXCEPTION(); }
|
||||
try { throw std::bad_alloc(); } catch (...) { FAIL_FAST_CAUGHT_EXCEPTION_MSG("train: %d", 42); }
|
||||
|
||||
try { try { throw std::bad_alloc(); } CATCH_THROW_NORMALIZED(); } catch (...) {}
|
||||
try { try { throw std::bad_alloc(); } CATCH_THROW_NORMALIZED_MSG("train: %d", 42); } 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 (...) {}
|
||||
|
||||
HRESULT hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::All, [&]
|
||||
{
|
||||
THROW_HR(E_FAIL);
|
||||
});
|
||||
|
||||
hr = wil::ResultFromException(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::None, [&]
|
||||
{
|
||||
});
|
||||
|
||||
hr = wil::ResultFromException([&]
|
||||
{
|
||||
});
|
||||
|
||||
wil::FailFastException(WI_DIAGNOSTICS_INFO, [&]
|
||||
{
|
||||
});
|
||||
}
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
TEST_CASE("ResultTests::ErrorMacros", "[result]")
|
||||
{
|
||||
REQUIRE_ERROR(FAIL_FAST());
|
||||
REQUIRE_ERROR(FAIL_FAST_IF(true));
|
||||
REQUIRE_ERROR(FAIL_FAST_IF_NULL(nullptr));
|
||||
|
||||
REQUIRE_NOERROR(FAIL_FAST_IF(false));
|
||||
REQUIRE_NOERROR(FAIL_FAST_IF_NULL(_ReturnAddress()));
|
||||
|
||||
REQUIRE_ERROR(FAIL_FAST_MSG("%d", 42));
|
||||
REQUIRE_ERROR(FAIL_FAST_IF_MSG(true, "%d", 42));
|
||||
REQUIRE_ERROR(FAIL_FAST_IF_NULL_MSG(nullptr, "%d", 42));
|
||||
|
||||
REQUIRE_NOERROR(FAIL_FAST_IF_MSG(false, "%d", 42));
|
||||
REQUIRE_NOERROR(FAIL_FAST_IF_NULL_MSG(_ReturnAddress(), "%d", 42));
|
||||
|
||||
//wil::g_pfnResultLoggingCallback = ResultMacrosLoggingCallback;
|
||||
SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
|
||||
REQUIRE_ERROR(__FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(FALSE));
|
||||
REQUIRE_NOERROR(__FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(TRUE));
|
||||
}
|
||||
|
||||
// The originate helper isn't compatible with CX so don't test it in that mode.
|
||||
#ifndef __cplusplus_winrt
|
||||
TEST_CASE("ResultTests::NoOriginationByDefault", "[result]")
|
||||
{
|
||||
::wil::SetOriginateErrorCallback(nullptr);
|
||||
wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
|
||||
|
||||
// We can't guarantee test order, so clear the error payload prior to starting
|
||||
SetRestrictedErrorInfo(nullptr);
|
||||
|
||||
[]() -> HRESULT
|
||||
{
|
||||
RETURN_HR(S_OK);
|
||||
}();
|
||||
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
try
|
||||
{
|
||||
THROW_HR(E_FAIL);
|
||||
}
|
||||
catch (...) {}
|
||||
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
[]() -> HRESULT
|
||||
{
|
||||
RETURN_HR(E_FAIL);
|
||||
}();
|
||||
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
|
||||
|
||||
[]() -> HRESULT
|
||||
{
|
||||
RETURN_IF_FAILED_EXPECTED(E_ACCESSDENIED);
|
||||
return S_OK;
|
||||
}();
|
||||
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
|
||||
}
|
||||
|
||||
TEST_CASE("ResultTests::AutomaticOriginationOnFailure", "[result]")
|
||||
{
|
||||
::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions);
|
||||
wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
|
||||
|
||||
// Make sure we don't start with an error payload
|
||||
SetRestrictedErrorInfo(nullptr);
|
||||
|
||||
// Success codes shouldn't originate.
|
||||
[]()
|
||||
{
|
||||
RETURN_HR(S_OK);
|
||||
}();
|
||||
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
|
||||
|
||||
auto validateOriginatedError = [&](HRESULT hrExpected)
|
||||
{
|
||||
wil::unique_bstr descriptionUnused;
|
||||
HRESULT existingHr = S_OK;
|
||||
wil::unique_bstr restrictedDescriptionUnused;
|
||||
wil::unique_bstr capabilitySidUnused;
|
||||
REQUIRE_SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused));
|
||||
REQUIRE(hrExpected == existingHr);
|
||||
};
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
// Throwing an error should originate.
|
||||
constexpr HRESULT thrownErrorCode = TYPE_E_ELEMENTNOTFOUND;
|
||||
try
|
||||
{
|
||||
THROW_HR(thrownErrorCode);
|
||||
}
|
||||
catch (...) {}
|
||||
REQUIRE(S_OK == GetRestrictedErrorInfo(&restrictedErrorInformation));
|
||||
validateOriginatedError(thrownErrorCode);
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
// Returning an error code should originate.
|
||||
static constexpr HRESULT returnedErrorCode = REGDB_E_CLASSNOTREG;
|
||||
[]()
|
||||
{
|
||||
RETURN_HR(returnedErrorCode);
|
||||
}();
|
||||
REQUIRE(S_OK == GetRestrictedErrorInfo(&restrictedErrorInformation));
|
||||
validateOriginatedError(returnedErrorCode);
|
||||
|
||||
// _EXPECTED errors should NOT originate.
|
||||
static constexpr HRESULT expectedErrorCode = E_ACCESSDENIED;
|
||||
[]()
|
||||
{
|
||||
RETURN_IF_FAILED_EXPECTED(expectedErrorCode);
|
||||
return S_OK;
|
||||
}();
|
||||
REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
|
||||
}
|
||||
#endif // __cplusplus_winrt
|
|
@ -0,0 +1,571 @@
|
|||
|
||||
#include "common.h"
|
||||
|
||||
#include <wil/safecast.h>
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
TEST_CASE("SafeCastTests::SafeCastThrowsTemplateCheck", "[safecast]")
|
||||
{
|
||||
// In all cases, a value of '1' should be cast-able to any signed or unsigned integral type without error
|
||||
SECTION("Unqualified char")
|
||||
{
|
||||
char orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
// wil::safe_cast<unsigned char> (orig); // No available conversion in intsafe
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
// wil::safe_cast<unsigned short> (orig); // No available conversion in intsafe
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
// wil::safe_cast<unsigned int> (orig); // No available conversion in intsafe
|
||||
wil::safe_cast<long> (orig);
|
||||
// wil::safe_cast<unsigned long> (orig); // No available conversion in intsafe
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
// wil::safe_cast<unsigned __int64> (orig); // No available conversion in intsafe
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
// wil::safe_cast<unsigned __int3264>(orig); // No available conversion in intsafe
|
||||
// wil::safe_cast<wchar_t> (orig); // No available conversion in intsafe
|
||||
}
|
||||
|
||||
SECTION("Signed char")
|
||||
{
|
||||
signed char orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
wil::safe_cast<unsigned char> (orig);
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
wil::safe_cast<unsigned short> (orig);
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
wil::safe_cast<unsigned int> (orig);
|
||||
wil::safe_cast<long> (orig);
|
||||
wil::safe_cast<unsigned long> (orig);
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
wil::safe_cast<unsigned __int64> (orig);
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
wil::safe_cast<unsigned __int3264>(orig);
|
||||
wil::safe_cast<wchar_t> (orig);
|
||||
}
|
||||
|
||||
SECTION("Unsigned char")
|
||||
{
|
||||
unsigned char orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
wil::safe_cast<unsigned char> (orig);
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
wil::safe_cast<unsigned short> (orig);
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
wil::safe_cast<unsigned int> (orig);
|
||||
wil::safe_cast<long> (orig);
|
||||
wil::safe_cast<unsigned long> (orig);
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
wil::safe_cast<unsigned __int64> (orig);
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
wil::safe_cast<unsigned __int3264>(orig);
|
||||
wil::safe_cast<wchar_t> (orig);
|
||||
}
|
||||
|
||||
SECTION("Unqualified short")
|
||||
{
|
||||
short orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
wil::safe_cast<unsigned char> (orig);
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
wil::safe_cast<unsigned short> (orig);
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
wil::safe_cast<unsigned int> (orig);
|
||||
wil::safe_cast<long> (orig);
|
||||
wil::safe_cast<unsigned long> (orig);
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
wil::safe_cast<unsigned __int64> (orig);
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
wil::safe_cast<unsigned __int3264>(orig);
|
||||
wil::safe_cast<wchar_t> (orig);
|
||||
}
|
||||
|
||||
SECTION("Signed short")
|
||||
{
|
||||
signed short orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
wil::safe_cast<unsigned char> (orig);
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
wil::safe_cast<unsigned short> (orig);
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
wil::safe_cast<unsigned int> (orig);
|
||||
wil::safe_cast<long> (orig);
|
||||
wil::safe_cast<unsigned long> (orig);
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
wil::safe_cast<unsigned __int64> (orig);
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
wil::safe_cast<unsigned __int3264>(orig);
|
||||
wil::safe_cast<wchar_t> (orig);
|
||||
}
|
||||
|
||||
SECTION("Unsigned short")
|
||||
{
|
||||
unsigned short orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
wil::safe_cast<unsigned char> (orig);
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
wil::safe_cast<unsigned short> (orig);
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
wil::safe_cast<unsigned int> (orig);
|
||||
wil::safe_cast<long> (orig);
|
||||
wil::safe_cast<unsigned long> (orig);
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
wil::safe_cast<unsigned __int64> (orig);
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
wil::safe_cast<unsigned __int3264>(orig);
|
||||
wil::safe_cast<wchar_t> (orig);
|
||||
}
|
||||
|
||||
SECTION("Unqualified int")
|
||||
{
|
||||
int orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
wil::safe_cast<unsigned char> (orig);
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
wil::safe_cast<unsigned short> (orig);
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
wil::safe_cast<unsigned int> (orig);
|
||||
wil::safe_cast<long> (orig);
|
||||
wil::safe_cast<unsigned long> (orig);
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
wil::safe_cast<unsigned __int64> (orig);
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
wil::safe_cast<unsigned __int3264>(orig);
|
||||
wil::safe_cast<wchar_t> (orig);
|
||||
}
|
||||
|
||||
SECTION("Signed int")
|
||||
{
|
||||
signed int orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
wil::safe_cast<unsigned char> (orig);
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
wil::safe_cast<unsigned short> (orig);
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
wil::safe_cast<unsigned int> (orig);
|
||||
wil::safe_cast<long> (orig);
|
||||
wil::safe_cast<unsigned long> (orig);
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
wil::safe_cast<unsigned __int64> (orig);
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
wil::safe_cast<unsigned __int3264>(orig);
|
||||
wil::safe_cast<wchar_t> (orig);
|
||||
}
|
||||
|
||||
SECTION("Unsigned int")
|
||||
{
|
||||
unsigned int orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
wil::safe_cast<unsigned char> (orig);
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
wil::safe_cast<unsigned short> (orig);
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
wil::safe_cast<unsigned int> (orig);
|
||||
wil::safe_cast<long> (orig);
|
||||
wil::safe_cast<unsigned long> (orig);
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
wil::safe_cast<unsigned __int64> (orig);
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
wil::safe_cast<unsigned __int3264>(orig);
|
||||
wil::safe_cast<wchar_t> (orig);
|
||||
}
|
||||
|
||||
SECTION("Unqualified long")
|
||||
{
|
||||
long orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
wil::safe_cast<unsigned char> (orig);
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
wil::safe_cast<unsigned short> (orig);
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
wil::safe_cast<unsigned int> (orig);
|
||||
wil::safe_cast<long> (orig);
|
||||
wil::safe_cast<unsigned long> (orig);
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
wil::safe_cast<unsigned __int64> (orig);
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
wil::safe_cast<unsigned __int3264>(orig);
|
||||
wil::safe_cast<wchar_t> (orig);
|
||||
}
|
||||
|
||||
SECTION("Unsigned log")
|
||||
{
|
||||
unsigned long orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
wil::safe_cast<unsigned char> (orig);
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
wil::safe_cast<unsigned short> (orig);
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
wil::safe_cast<unsigned int> (orig);
|
||||
wil::safe_cast<long> (orig);
|
||||
wil::safe_cast<unsigned long> (orig);
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
wil::safe_cast<unsigned __int64> (orig);
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
wil::safe_cast<unsigned __int3264>(orig);
|
||||
wil::safe_cast<wchar_t> (orig);
|
||||
}
|
||||
|
||||
SECTION("Unqualified int64")
|
||||
{
|
||||
__int64 orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
wil::safe_cast<unsigned char> (orig);
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
wil::safe_cast<unsigned short> (orig);
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
wil::safe_cast<unsigned int> (orig);
|
||||
wil::safe_cast<long> (orig);
|
||||
wil::safe_cast<unsigned long> (orig);
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
wil::safe_cast<unsigned __int64> (orig);
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
wil::safe_cast<unsigned __int3264>(orig);
|
||||
wil::safe_cast<wchar_t> (orig);
|
||||
}
|
||||
|
||||
SECTION("Signed int64")
|
||||
{
|
||||
signed __int64 orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
wil::safe_cast<unsigned char> (orig);
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
wil::safe_cast<unsigned short> (orig);
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
wil::safe_cast<unsigned int> (orig);
|
||||
wil::safe_cast<long> (orig);
|
||||
wil::safe_cast<unsigned long> (orig);
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
wil::safe_cast<unsigned __int64> (orig);
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
wil::safe_cast<unsigned __int3264>(orig);
|
||||
wil::safe_cast<wchar_t> (orig);
|
||||
}
|
||||
|
||||
SECTION("Unsigned int64")
|
||||
{
|
||||
unsigned __int64 orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
wil::safe_cast<unsigned char> (orig);
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
wil::safe_cast<unsigned short> (orig);
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
wil::safe_cast<unsigned int> (orig);
|
||||
wil::safe_cast<long> (orig);
|
||||
wil::safe_cast<unsigned long> (orig);
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
wil::safe_cast<unsigned __int64> (orig);
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
wil::safe_cast<unsigned __int3264>(orig);
|
||||
wil::safe_cast<wchar_t> (orig);
|
||||
}
|
||||
|
||||
SECTION("Unqualified int3264")
|
||||
{
|
||||
__int3264 orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
wil::safe_cast<unsigned char> (orig);
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
wil::safe_cast<unsigned short> (orig);
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
wil::safe_cast<unsigned int> (orig);
|
||||
wil::safe_cast<long> (orig);
|
||||
wil::safe_cast<unsigned long> (orig);
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
wil::safe_cast<unsigned __int64> (orig);
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
wil::safe_cast<unsigned __int3264>(orig);
|
||||
wil::safe_cast<wchar_t> (orig);
|
||||
}
|
||||
|
||||
SECTION("Unsigned int3264")
|
||||
{
|
||||
unsigned __int3264 orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
wil::safe_cast<unsigned char> (orig);
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
wil::safe_cast<unsigned short> (orig);
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
wil::safe_cast<unsigned int> (orig);
|
||||
wil::safe_cast<long> (orig);
|
||||
wil::safe_cast<unsigned long> (orig);
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
wil::safe_cast<unsigned __int64> (orig);
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
wil::safe_cast<unsigned __int3264>(orig);
|
||||
wil::safe_cast<wchar_t> (orig);
|
||||
}
|
||||
|
||||
SECTION("wchar_t")
|
||||
{
|
||||
wchar_t orig = 1;
|
||||
wil::safe_cast<char> (orig);
|
||||
wil::safe_cast<signed char> (orig);
|
||||
wil::safe_cast<unsigned char> (orig);
|
||||
wil::safe_cast<short> (orig);
|
||||
wil::safe_cast<signed short> (orig);
|
||||
wil::safe_cast<unsigned short> (orig);
|
||||
wil::safe_cast<int> (orig);
|
||||
wil::safe_cast<signed int> (orig);
|
||||
wil::safe_cast<unsigned int> (orig);
|
||||
wil::safe_cast<long> (orig);
|
||||
wil::safe_cast<unsigned long> (orig);
|
||||
wil::safe_cast<__int64> (orig);
|
||||
wil::safe_cast<signed __int64> (orig);
|
||||
wil::safe_cast<unsigned __int64> (orig);
|
||||
wil::safe_cast<__int3264> (orig);
|
||||
wil::safe_cast<unsigned __int3264>(orig);
|
||||
wil::safe_cast<wchar_t> (orig);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("SafeCastTests::SafeCastFailFastSyntaxCheck", "[safecast]")
|
||||
{
|
||||
SECTION("safe_cast_failfast safe")
|
||||
{
|
||||
INT i = INT_MAX;
|
||||
LONG l = wil::safe_cast_failfast<LONG>(i);
|
||||
REQUIRE(l == INT_MAX);
|
||||
}
|
||||
|
||||
SECTION("safe_cast_failfast unsafe")
|
||||
{
|
||||
INT i = 0;
|
||||
SHORT s = wil::safe_cast_failfast<SHORT>(i);
|
||||
REQUIRE(s == 0);
|
||||
}
|
||||
|
||||
SECTION("safe_cast_failfast unsafe to wchar_t")
|
||||
{
|
||||
INT i = 0;
|
||||
wchar_t wc = wil::safe_cast_failfast<wchar_t>(i);
|
||||
REQUIRE(wc == 0);
|
||||
}
|
||||
|
||||
SECTION("safe_cast_failfast unsafe from wchar_t")
|
||||
{
|
||||
wchar_t wc = 0;
|
||||
unsigned char uc = wil::safe_cast_failfast<unsigned char>(wc);
|
||||
REQUIRE(uc == 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("SafeCastTests::SafeCastNoThrowSyntaxCheck", "[safecast]")
|
||||
{
|
||||
SECTION("safe_cast_nothrow safe")
|
||||
{
|
||||
INT i = INT_MAX;
|
||||
LONG l = wil::safe_cast_nothrow<LONG>(i);
|
||||
REQUIRE(l == INT_MAX);
|
||||
}
|
||||
|
||||
// safe_cast_nothrow one parameter unsafe, throws compiler error as expected
|
||||
// {
|
||||
// __int64 i64 = 0;
|
||||
// int i = wil::safe_cast_nothrow<int>(i64);
|
||||
// }
|
||||
|
||||
SECTION("safe_cast_nothrow two parameter potentially unsafe due to usage of variable sized types")
|
||||
{
|
||||
SIZE_T st = 0;
|
||||
UINT ui;
|
||||
auto result = wil::safe_cast_nothrow<UINT>(st, &ui);
|
||||
REQUIRE_SUCCEEDED(result);
|
||||
REQUIRE(ui == 0);
|
||||
}
|
||||
|
||||
// safe_cast_nothrow two parameter known safe, throws compiler error as expected
|
||||
// {
|
||||
// unsigned char uc = 0;
|
||||
// unsigned short us;
|
||||
// auto result = wil::safe_cast_nothrow(uc, &us);
|
||||
// }
|
||||
|
||||
SECTION("safe_cast_nothrow unsafe")
|
||||
{
|
||||
INT i = 0;
|
||||
SHORT s;
|
||||
auto result = wil::safe_cast_nothrow<SHORT>(i, &s);
|
||||
REQUIRE_SUCCEEDED(result);
|
||||
REQUIRE(s == 0);
|
||||
}
|
||||
|
||||
SECTION("safe_cast_nothrow unsafe to wchar_t")
|
||||
{
|
||||
INT i = 0;
|
||||
wchar_t wc;
|
||||
auto result = wil::safe_cast_nothrow<wchar_t>(i, &wc);
|
||||
REQUIRE_SUCCEEDED(result);
|
||||
REQUIRE(wc == 0);
|
||||
}
|
||||
|
||||
SECTION("safe_cast_nothrow unsafe from wchar_t")
|
||||
{
|
||||
wchar_t wc = 0;
|
||||
unsigned char uc;
|
||||
auto result = wil::safe_cast_nothrow<unsigned char>(wc, &uc);
|
||||
REQUIRE_SUCCEEDED(result);
|
||||
REQUIRE(uc == 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("SafeCastTests::SafeCastNoFailures", "[safecast]")
|
||||
{
|
||||
SECTION("INT -> LONG")
|
||||
{
|
||||
INT i = INT_MAX;
|
||||
LONG l = wil::safe_cast_nothrow<LONG>(i);
|
||||
REQUIRE(l == INT_MAX);
|
||||
}
|
||||
|
||||
SECTION("LONG -> INT")
|
||||
{
|
||||
LONG l = LONG_MAX;
|
||||
INT i = wil::safe_cast_nothrow<INT>(l);
|
||||
REQUIRE(i == LONG_MAX);
|
||||
}
|
||||
|
||||
SECTION("INT -> UINT")
|
||||
{
|
||||
INT i = INT_MAX;
|
||||
UINT ui = wil::safe_cast_failfast<UINT>(i);
|
||||
REQUIRE(ui == INT_MAX);
|
||||
}
|
||||
|
||||
SECTION("SIZE_T -> SIZE_T")
|
||||
{
|
||||
SIZE_T st = SIZE_T_MAX;
|
||||
SIZE_T st2 = wil::safe_cast_failfast<SIZE_T>(st);
|
||||
REQUIRE(st2 == SIZE_T_MAX);
|
||||
}
|
||||
|
||||
SECTION("wchar_t -> uint")
|
||||
{
|
||||
wchar_t wc = 0;
|
||||
UINT ui = wil::safe_cast_failfast<UINT>(wc);
|
||||
REQUIRE(ui == 0);
|
||||
}
|
||||
|
||||
SECTION("wchar_t -> unsigned char")
|
||||
{
|
||||
wchar_t wc = 0;
|
||||
unsigned char uc = wil::safe_cast_failfast<unsigned char>(wc);
|
||||
REQUIRE(uc == 0);
|
||||
auto result = wil::safe_cast_nothrow<unsigned char>(wc, &uc);
|
||||
REQUIRE_SUCCEEDED(result);
|
||||
}
|
||||
|
||||
SECTION("uint -> wchar_t")
|
||||
{
|
||||
UINT ui = 0;
|
||||
wchar_t wc = wil::safe_cast_failfast<wchar_t>(ui);
|
||||
REQUIRE(wc == 0);
|
||||
auto result = wil::safe_cast_nothrow<wchar_t>(ui, &wc);
|
||||
REQUIRE_SUCCEEDED(result);
|
||||
}
|
||||
|
||||
#ifndef _WIN64
|
||||
SECTION("SIZE_T -> UINT")
|
||||
{
|
||||
SIZE_T st = SIZE_T_MAX;
|
||||
UINT ui = wil::safe_cast_nothrow<UINT>(st);
|
||||
REQUIRE(ui == SIZE_T_MAX);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("SafeCastTests::SafeCastNoThrowFail", "[safecast]")
|
||||
{
|
||||
SECTION("size_t -> short")
|
||||
{
|
||||
size_t st = SIZE_T_MAX;
|
||||
short s;
|
||||
REQUIRE_FAILED(wil::safe_cast_nothrow(st, &s));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
TEST_CASE("SafeCastTests::SafeCastExpectFailFast", "[safecast]")
|
||||
{
|
||||
// Template for safe_cast fail fast tests, fill out more instances needed
|
||||
witest::TestFailureCache failures;
|
||||
|
||||
failures.clear();
|
||||
{
|
||||
size_t st = SIZE_T_MAX;
|
||||
REQUIRE_CRASH(wil::safe_cast_failfast<short>(st));
|
||||
REQUIRE(failures.size() == 1);
|
||||
}
|
||||
|
||||
failures.clear();
|
||||
{
|
||||
size_t st = SIZE_T_MAX;
|
||||
REQUIRE_THROWS(wil::safe_cast<short>(st));
|
||||
REQUIRE(failures.size() == 1);
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
#include <wil/stl.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#ifndef WIL_ENABLE_EXCEPTIONS
|
||||
#error STL tests require exceptions
|
||||
#endif
|
||||
|
||||
struct dummy
|
||||
{
|
||||
char value;
|
||||
};
|
||||
|
||||
// Specialize std::allocator<> so that we don't actually allocate/deallocate memory
|
||||
dummy g_memoryBuffer[256];
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct allocator<dummy>
|
||||
{
|
||||
using value_type = dummy;
|
||||
using size_type = std::size_t;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
|
||||
dummy* allocate(std::size_t count)
|
||||
{
|
||||
REQUIRE(count <= std::size(g_memoryBuffer));
|
||||
return g_memoryBuffer;
|
||||
}
|
||||
|
||||
void deallocate(dummy* ptr, std::size_t count)
|
||||
{
|
||||
for (std::size_t i = 0; i < count; ++i)
|
||||
{
|
||||
REQUIRE(ptr[i].value == 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("StlTests::TestSecureAllocator", "[stl][secure_allocator]")
|
||||
{
|
||||
{
|
||||
wil::secure_vector<dummy> sensitiveBytes(32, dummy{ 'a' });
|
||||
}
|
||||
}
|
|
@ -0,0 +1,288 @@
|
|||
|
||||
#include <wil/token_helpers.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
TEST_CASE("TokenHelpersTests::VerifyOpenCurrentAccessTokenNoThrow", "[token_helpers]")
|
||||
{
|
||||
// Open current thread/process access token
|
||||
wil::unique_handle token;
|
||||
REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token));
|
||||
REQUIRE(token != nullptr);
|
||||
|
||||
REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ));
|
||||
REQUIRE(token != nullptr);
|
||||
|
||||
REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ, wil::OpenThreadTokenAs::Current));
|
||||
REQUIRE(token != nullptr);
|
||||
|
||||
REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ, wil::OpenThreadTokenAs::Self));
|
||||
REQUIRE(token != nullptr);
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
TEST_CASE("TokenHelpersTests::VerifyOpenCurrentAccessToken", "[token_helpers]")
|
||||
{
|
||||
// Open current thread/process access token
|
||||
wil::unique_handle token(wil::open_current_access_token());
|
||||
REQUIRE(token != nullptr);
|
||||
|
||||
token = wil::open_current_access_token(TOKEN_READ);
|
||||
REQUIRE(token != nullptr);
|
||||
|
||||
token = wil::open_current_access_token(TOKEN_READ, wil::OpenThreadTokenAs::Current);
|
||||
REQUIRE(token != nullptr);
|
||||
|
||||
token = wil::open_current_access_token(TOKEN_READ, wil::OpenThreadTokenAs::Self);
|
||||
REQUIRE(token != nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationNoThrow", "[token_helpers]")
|
||||
{
|
||||
SECTION("Passing a null token")
|
||||
{
|
||||
wistd::unique_ptr<TOKEN_USER> tokenInfo;
|
||||
REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, nullptr));
|
||||
REQUIRE(tokenInfo != nullptr);
|
||||
}
|
||||
|
||||
SECTION("Passing a non null token, since it a fake token there is no tokenInfo and hence should fail, code path is correct")
|
||||
{
|
||||
HANDLE faketoken = GetStdHandle(STD_INPUT_HANDLE);
|
||||
wistd::unique_ptr<TOKEN_USER> tokenInfo;
|
||||
REQUIRE_FAILED(wil::get_token_information_nothrow(tokenInfo, faketoken));
|
||||
}
|
||||
}
|
||||
|
||||
// Pseudo tokens can be passed to token APIs and avoid the handle allocations
|
||||
// making use more efficient.
|
||||
TEST_CASE("TokenHelpersTests::DemonstrateUseWithPseudoTokens", "[token_helpers]")
|
||||
{
|
||||
wistd::unique_ptr<TOKEN_USER> tokenInfo;
|
||||
REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, GetCurrentProcessToken()));
|
||||
REQUIRE(tokenInfo != nullptr);
|
||||
|
||||
REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, GetCurrentThreadEffectiveToken()));
|
||||
REQUIRE(tokenInfo != nullptr);
|
||||
|
||||
// No thread token by default, this should fail
|
||||
REQUIRE_FAILED(wil::get_token_information_nothrow(tokenInfo, GetCurrentThreadToken()));
|
||||
REQUIRE(tokenInfo == nullptr);
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformation", "[token_helpers]")
|
||||
{
|
||||
// Passing a null token
|
||||
wistd::unique_ptr<TOKEN_USER> tokenInfo(wil::get_token_information<TOKEN_USER>(nullptr));
|
||||
REQUIRE(tokenInfo != nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
// This fails with 'ERROR_NO_SUCH_LOGON_SESSION' on the CI machines, so disable
|
||||
#ifndef WIL_FAST_BUILD
|
||||
TEST_CASE("TokenHelpersTests::VerifyLinkedToken", "[token_helpers]")
|
||||
{
|
||||
wil::unique_token_linked_token theToken;
|
||||
REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(theToken, nullptr));
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
REQUIRE_NOTHROW(wil::get_linked_token_information());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
bool IsImpersonating()
|
||||
{
|
||||
wil::unique_handle token;
|
||||
if (!::OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token))
|
||||
{
|
||||
WI_ASSERT(::GetLastError() == ERROR_NO_TOKEN);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
wil::unique_handle GetTokenToImpersonate()
|
||||
{
|
||||
wil::unique_handle processToken;
|
||||
FAIL_FAST_IF_WIN32_BOOL_FALSE(::OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &processToken));
|
||||
|
||||
wil::unique_handle impersonateToken;
|
||||
FAIL_FAST_IF_WIN32_BOOL_FALSE(::DuplicateToken(processToken.get(), SecurityImpersonation, &impersonateToken));
|
||||
|
||||
return impersonateToken;
|
||||
}
|
||||
|
||||
TEST_CASE("TokenHelpersTests::VerifyResetThreadTokenNoThrow", "[token_helpers]")
|
||||
{
|
||||
auto impersonateToken = GetTokenToImpersonate();
|
||||
|
||||
// Set the thread into a known state - no token.
|
||||
wil::unique_token_reverter clearThreadToken;
|
||||
REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadToken));
|
||||
REQUIRE_FALSE(IsImpersonating());
|
||||
|
||||
// Set a token on the thread - the process token, guaranteed to be friendly
|
||||
wil::unique_token_reverter setThreadToken1;
|
||||
REQUIRE_SUCCEEDED(wil::impersonate_token_nothrow(impersonateToken.get(), setThreadToken1));
|
||||
REQUIRE(IsImpersonating());
|
||||
|
||||
SECTION("Clear the token again, should be not impersonating, explicit reset")
|
||||
{
|
||||
wil::unique_token_reverter clearThreadAgain;
|
||||
REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadAgain));
|
||||
REQUIRE_FALSE(IsImpersonating());
|
||||
clearThreadAgain.reset();
|
||||
REQUIRE(IsImpersonating());
|
||||
}
|
||||
|
||||
SECTION("Clear the token again, should be not impersonating, dtor reset")
|
||||
{
|
||||
wil::unique_token_reverter clearThreadAgain;
|
||||
REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadAgain));
|
||||
REQUIRE_FALSE(IsImpersonating());
|
||||
}
|
||||
REQUIRE(IsImpersonating());
|
||||
|
||||
// Clear what we were impersonating
|
||||
setThreadToken1.reset();
|
||||
REQUIRE_FALSE(IsImpersonating());
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
TEST_CASE("TokenHelpersTests::VerifyResetThreadToken", "[token_helpers]")
|
||||
{
|
||||
auto impersonateToken = GetTokenToImpersonate();
|
||||
|
||||
// Set the thread into a known state - no token.
|
||||
auto clearThreadToken = wil::run_as_self();
|
||||
REQUIRE_FALSE(IsImpersonating());
|
||||
|
||||
// Set a token on the thread - the process token, guaranteed to be friendly
|
||||
auto setThreadToken1 = wil::impersonate_token(impersonateToken.get());
|
||||
REQUIRE(IsImpersonating());
|
||||
|
||||
SECTION("Clear the token again, should be not impersonating, explicit reset")
|
||||
{
|
||||
auto clearThreadAgain = wil::run_as_self();
|
||||
REQUIRE_FALSE(IsImpersonating());
|
||||
clearThreadAgain.reset();
|
||||
REQUIRE(IsImpersonating());
|
||||
}
|
||||
|
||||
SECTION("Clear the token again, should be not impersonating, dtor reset")
|
||||
{
|
||||
auto clearThreadAgain = wil::run_as_self();
|
||||
REQUIRE_FALSE(IsImpersonating());
|
||||
}
|
||||
REQUIRE(IsImpersonating());
|
||||
|
||||
// Clear what we were impersonating
|
||||
setThreadToken1.reset();
|
||||
REQUIRE_FALSE(IsImpersonating());
|
||||
}
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
template <typename T, wistd::enable_if_t<!wil::details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
|
||||
void TestGetTokenInfoForCurrentThread()
|
||||
{
|
||||
wistd::unique_ptr<T> tokenInfo;
|
||||
const auto hr = wil::get_token_information_nothrow(tokenInfo, nullptr);
|
||||
REQUIRE(S_OK == hr);
|
||||
}
|
||||
|
||||
template <typename T, wistd::enable_if_t<wil::details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr>
|
||||
void TestGetTokenInfoForCurrentThread()
|
||||
{
|
||||
T tokenInfo{};
|
||||
const auto hr = wil::get_token_information_nothrow(&tokenInfo, nullptr);
|
||||
REQUIRE(S_OK == hr);
|
||||
}
|
||||
|
||||
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformation2", "[token_helpers]")
|
||||
{
|
||||
// Variable sized cases
|
||||
TestGetTokenInfoForCurrentThread<TOKEN_ACCESS_INFORMATION>();
|
||||
TestGetTokenInfoForCurrentThread<TOKEN_APPCONTAINER_INFORMATION>();
|
||||
TestGetTokenInfoForCurrentThread<TOKEN_DEFAULT_DACL>();
|
||||
TestGetTokenInfoForCurrentThread<TOKEN_GROUPS_AND_PRIVILEGES>();
|
||||
TestGetTokenInfoForCurrentThread<TOKEN_MANDATORY_LABEL>();
|
||||
TestGetTokenInfoForCurrentThread<TOKEN_OWNER>();
|
||||
TestGetTokenInfoForCurrentThread<TOKEN_PRIMARY_GROUP>();
|
||||
TestGetTokenInfoForCurrentThread<TOKEN_PRIVILEGES>();
|
||||
TestGetTokenInfoForCurrentThread<TOKEN_USER>();
|
||||
|
||||
// Fixed size and reports size using ERROR_INSUFFICIENT_BUFFER (perf opportunity, ignore second allocation)
|
||||
TestGetTokenInfoForCurrentThread<TOKEN_ELEVATION_TYPE>();
|
||||
TestGetTokenInfoForCurrentThread<TOKEN_MANDATORY_POLICY>();
|
||||
TestGetTokenInfoForCurrentThread<TOKEN_ORIGIN>();
|
||||
TestGetTokenInfoForCurrentThread<TOKEN_SOURCE>();
|
||||
TestGetTokenInfoForCurrentThread<TOKEN_STATISTICS>();
|
||||
TestGetTokenInfoForCurrentThread<TOKEN_TYPE>();
|
||||
}
|
||||
|
||||
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationBadLength", "[token_helpers]")
|
||||
{
|
||||
// Fixed size and reports size using ERROR_BAD_LENGTH (bug)
|
||||
TestGetTokenInfoForCurrentThread<TOKEN_ELEVATION>();
|
||||
}
|
||||
|
||||
TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationSecurityImpersonationLevelErrorCases", "[token_helpers]")
|
||||
{
|
||||
SECURITY_IMPERSONATION_LEVEL tokenInfo{};
|
||||
|
||||
// SECURITY_IMPERSONATION_LEVEL does not support the effective token when it is implicit.
|
||||
// Demonstrate the error return in that case.
|
||||
REQUIRE(E_INVALIDARG == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadEffectiveToken()));
|
||||
|
||||
// Using an explicit token is supported but returns ERROR_NO_TOKEN when there is no
|
||||
// impersonation token be sure to use RETURN_IF_FAILED_EXPECTED() and don't use
|
||||
// the exception forms if this case is not expected.
|
||||
REQUIRE(HRESULT_FROM_WIN32(ERROR_NO_TOKEN) == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadToken()));
|
||||
|
||||
// Setup the impersonation token that SECURITY_IMPERSONATION_LEVEL requires.
|
||||
FAIL_FAST_IF_WIN32_BOOL_FALSE(ImpersonateSelf(SecurityIdentification));
|
||||
TestGetTokenInfoForCurrentThread<SECURITY_IMPERSONATION_LEVEL>();
|
||||
|
||||
REQUIRE(S_OK == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadToken()));
|
||||
|
||||
RevertToSelf();
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
|
||||
TEST_CASE("TokenHelpersTests::VerifyGetTokenInfo", "[token_helpers]")
|
||||
{
|
||||
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_APPCONTAINER_INFORMATION>());
|
||||
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_ELEVATION_TYPE>());
|
||||
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_MANDATORY_POLICY>());
|
||||
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_ORIGIN>());
|
||||
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_STATISTICS>());
|
||||
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_TYPE>());
|
||||
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_ELEVATION>());
|
||||
|
||||
// check a non-pointer size value to make sure the whole struct is returned.
|
||||
DWORD resultSize{};
|
||||
TOKEN_SOURCE ts{};
|
||||
auto tokenSource = wil::get_token_information<TOKEN_SOURCE>();
|
||||
GetTokenInformation(GetCurrentThreadEffectiveToken(), TokenSource, &ts, sizeof(ts), &resultSize);
|
||||
REQUIRE(memcmp(&ts, &tokenSource, sizeof(ts)) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("TokenHelpersTests::VerifyGetTokenInfoFailFast", "[token_helpers]")
|
||||
{
|
||||
// fixed size
|
||||
REQUIRE_NOTHROW(wil::get_token_information_failfast<TOKEN_APPCONTAINER_INFORMATION>());
|
||||
// variable size
|
||||
REQUIRE_NOTHROW(wil::get_token_information_failfast<TOKEN_OWNER>());
|
||||
}
|
||||
|
||||
TEST_CASE("TokenHelpersTests::Verify_impersonate_token", "[token_helpers]")
|
||||
{
|
||||
auto impersonationToken = wil::impersonate_token();
|
||||
REQUIRE_NOTHROW(wil::get_token_information<TOKEN_TYPE>());
|
||||
}
|
||||
#endif // WIL_ENABLE_EXCEPTIONS
|
|
@ -0,0 +1,266 @@
|
|||
|
||||
#include <wil/winrt.h>
|
||||
#include <wrl/implements.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
using namespace ABI::Windows::Foundation;
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
namespace wiltest
|
||||
{
|
||||
class AbiTestEventSender WrlFinal : public RuntimeClass<
|
||||
RuntimeClassFlags<RuntimeClassType::WinRtClassicComMix>,
|
||||
IClosable,
|
||||
IMemoryBufferReference,
|
||||
FtmBase>
|
||||
{
|
||||
public:
|
||||
|
||||
// IMemoryBufferReference
|
||||
IFACEMETHODIMP get_Capacity(_Out_ UINT32* value)
|
||||
{
|
||||
*value = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP add_Closed(
|
||||
_In_ ITypedEventHandler<IMemoryBufferReference*, IInspectable*>* handler,
|
||||
_Out_ ::EventRegistrationToken* token)
|
||||
{
|
||||
return m_closedEvent.Add(handler, token);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP remove_Closed(::EventRegistrationToken token)
|
||||
{
|
||||
return m_closedEvent.Remove(token);
|
||||
}
|
||||
|
||||
// IClosable
|
||||
IFACEMETHODIMP Close()
|
||||
{
|
||||
RETURN_IF_FAILED(m_closedEvent.InvokeAll(this, nullptr));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
Microsoft::WRL::EventSource<ITypedEventHandler<IMemoryBufferReference*, IInspectable*>> m_closedEvent;
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenEventSubscribe", "[winrt][unique_winrt_event_token]")
|
||||
{
|
||||
ComPtr<IMemoryBufferReference> testEventSender;
|
||||
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
|
||||
ComPtr<IClosable> closable;
|
||||
testEventSender.As(&closable);
|
||||
|
||||
int timesInvoked = 0;
|
||||
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
|
||||
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
|
||||
([×Invoked](IInspectable*, IInspectable*)
|
||||
{
|
||||
timesInvoked++;
|
||||
return S_OK;
|
||||
});
|
||||
REQUIRE(timesInvoked == 0);
|
||||
|
||||
{
|
||||
wil::unique_winrt_event_token<IMemoryBufferReference> token;
|
||||
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token));
|
||||
REQUIRE(static_cast<bool>(token));
|
||||
REQUIRE_SUCCEEDED(closable->Close());
|
||||
REQUIRE(timesInvoked == 1);
|
||||
}
|
||||
|
||||
REQUIRE_SUCCEEDED(closable->Close());
|
||||
REQUIRE(timesInvoked == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenEarlyReset", "[winrt][unique_winrt_event_token]")
|
||||
{
|
||||
ComPtr<IMemoryBufferReference> testEventSender;
|
||||
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
|
||||
ComPtr<IClosable> closable;
|
||||
testEventSender.As(&closable);
|
||||
|
||||
int timesInvoked = 0;
|
||||
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
|
||||
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
|
||||
([×Invoked](IInspectable*, IInspectable*)
|
||||
{
|
||||
timesInvoked++;
|
||||
return S_OK;
|
||||
});
|
||||
REQUIRE(timesInvoked == 0);
|
||||
|
||||
wil::unique_winrt_event_token<IMemoryBufferReference> token;
|
||||
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token));
|
||||
REQUIRE(static_cast<bool>(token));
|
||||
REQUIRE_SUCCEEDED(closable->Close());
|
||||
REQUIRE(timesInvoked == 1);
|
||||
|
||||
token.reset();
|
||||
|
||||
REQUIRE_SUCCEEDED(closable->Close());
|
||||
REQUIRE(timesInvoked == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenMoveTokenToDifferentScope", "[winrt][unique_winrt_event_token]")
|
||||
{
|
||||
ComPtr<IMemoryBufferReference> testEventSender;
|
||||
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
|
||||
ComPtr<IClosable> closable;
|
||||
testEventSender.As(&closable);
|
||||
|
||||
int timesInvoked = 0;
|
||||
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
|
||||
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
|
||||
([×Invoked](IInspectable*, IInspectable*)
|
||||
{
|
||||
timesInvoked++;
|
||||
return S_OK;
|
||||
});
|
||||
REQUIRE(timesInvoked == 0);
|
||||
|
||||
wil::unique_winrt_event_token<IMemoryBufferReference> outerToken;
|
||||
REQUIRE_FALSE(static_cast<bool>(outerToken));
|
||||
{
|
||||
wil::unique_winrt_event_token<IMemoryBufferReference> token;
|
||||
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token));
|
||||
REQUIRE(static_cast<bool>(token));
|
||||
REQUIRE_SUCCEEDED(closable->Close());
|
||||
REQUIRE(timesInvoked == 1);
|
||||
|
||||
outerToken = std::move(token);
|
||||
REQUIRE_FALSE(static_cast<bool>(token));
|
||||
REQUIRE(static_cast<bool>(outerToken));
|
||||
}
|
||||
|
||||
REQUIRE_SUCCEEDED(closable->Close());
|
||||
REQUIRE(timesInvoked == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenMoveConstructor", "[winrt][unique_winrt_event_token]")
|
||||
{
|
||||
ComPtr<IMemoryBufferReference> testEventSender;
|
||||
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
|
||||
ComPtr<IClosable> closable;
|
||||
testEventSender.As(&closable);
|
||||
|
||||
int timesInvoked = 0;
|
||||
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
|
||||
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
|
||||
([×Invoked](IInspectable*, IInspectable*)
|
||||
{
|
||||
timesInvoked++;
|
||||
return S_OK;
|
||||
});
|
||||
REQUIRE(timesInvoked == 0);
|
||||
|
||||
wil::unique_winrt_event_token<IMemoryBufferReference> firstToken;
|
||||
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &firstToken));
|
||||
REQUIRE(static_cast<bool>(firstToken));
|
||||
closable->Close();
|
||||
REQUIRE(timesInvoked == 1);
|
||||
|
||||
wil::unique_winrt_event_token<IMemoryBufferReference> secondToken(std::move(firstToken));
|
||||
REQUIRE_FALSE(static_cast<bool>(firstToken));
|
||||
REQUIRE(static_cast<bool>(secondToken));
|
||||
|
||||
closable->Close();
|
||||
REQUIRE(timesInvoked == 2);
|
||||
|
||||
firstToken.reset();
|
||||
closable->Close();
|
||||
REQUIRE(timesInvoked == 3);
|
||||
|
||||
secondToken.reset();
|
||||
closable->Close();
|
||||
REQUIRE(timesInvoked == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenReleaseAndReattachToNewWrapper", "[winrt][unique_winrt_event_token]")
|
||||
{
|
||||
ComPtr<IMemoryBufferReference> testEventSender;
|
||||
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
|
||||
ComPtr<IClosable> closable;
|
||||
testEventSender.As(&closable);
|
||||
|
||||
int timesInvoked = 0;
|
||||
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
|
||||
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
|
||||
([×Invoked](IInspectable*, IInspectable*)
|
||||
{
|
||||
timesInvoked++;
|
||||
return S_OK;
|
||||
});
|
||||
REQUIRE(timesInvoked == 0);
|
||||
|
||||
wil::unique_winrt_event_token<IMemoryBufferReference> firstToken;
|
||||
REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &firstToken));
|
||||
REQUIRE(static_cast<bool>(firstToken));
|
||||
REQUIRE_SUCCEEDED(closable->Close());
|
||||
REQUIRE(timesInvoked == 1);
|
||||
|
||||
::EventRegistrationToken rawToken = firstToken.release();
|
||||
REQUIRE_FALSE(static_cast<bool>(firstToken));
|
||||
REQUIRE(rawToken.value != 0);
|
||||
|
||||
REQUIRE_SUCCEEDED(closable->Close());
|
||||
REQUIRE(timesInvoked == 2);
|
||||
|
||||
wil::unique_winrt_event_token<IMemoryBufferReference> secondToken(
|
||||
rawToken, testEventSender.Get(), &IMemoryBufferReference::remove_Closed);
|
||||
|
||||
REQUIRE_SUCCEEDED(closable->Close());
|
||||
REQUIRE(timesInvoked == 3);
|
||||
|
||||
secondToken.reset();
|
||||
REQUIRE_SUCCEEDED(closable->Close());
|
||||
REQUIRE(timesInvoked == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenPolicyVariants", "[winrt][unique_winrt_event_token]")
|
||||
{
|
||||
ComPtr<IMemoryBufferReference> testEventSender;
|
||||
REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender));
|
||||
ComPtr<IClosable> closable;
|
||||
testEventSender.As(&closable);
|
||||
|
||||
int timesInvoked = 0;
|
||||
auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>,
|
||||
ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>>
|
||||
([×Invoked](IInspectable*, IInspectable*)
|
||||
{
|
||||
timesInvoked++;
|
||||
return S_OK;
|
||||
});
|
||||
REQUIRE(timesInvoked == 0);
|
||||
|
||||
{
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
auto exceptionPolicyToken = WI_MakeUniqueWinRtEventToken(Closed, testEventSender.Get(), handler.Get());
|
||||
REQUIRE(static_cast<bool>(exceptionPolicyToken));
|
||||
#endif
|
||||
|
||||
auto failFastPolicyToken = WI_MakeUniqueWinRtEventTokenFailFast(Closed, testEventSender.Get(), handler.Get());
|
||||
REQUIRE(static_cast<bool>(failFastPolicyToken));
|
||||
|
||||
REQUIRE_SUCCEEDED(closable->Close());
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
REQUIRE(timesInvoked == 2);
|
||||
#else
|
||||
REQUIRE(timesInvoked == 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
REQUIRE_SUCCEEDED(closable->Close());
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
REQUIRE(timesInvoked == 2);
|
||||
#else
|
||||
REQUIRE(timesInvoked == 1);
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,516 @@
|
|||
|
||||
#include <memory> // For shared_event_watcher
|
||||
#include <windows.h> // filesystem.h includes PathCch.h which includes winnt.h, which will complain about missing target architecture
|
||||
#include <wil/filesystem.h>
|
||||
#include <wil/registry.h>
|
||||
#include <wil/resource.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
TEST_CASE("EventWatcherTests::Construction", "[resource][event_watcher]")
|
||||
{
|
||||
SECTION("Create unique_event_watcher_nothrow without event")
|
||||
{
|
||||
auto watcher = wil::make_event_watcher_nothrow([]{});
|
||||
REQUIRE(watcher != nullptr);
|
||||
}
|
||||
|
||||
SECTION("Create unique_event_watcher_nothrow with unique_event_nothrow")
|
||||
{
|
||||
wil::unique_event_nothrow eventToPass;
|
||||
FAIL_FAST_IF_FAILED(eventToPass.create(wil::EventOptions::None));
|
||||
auto watcher = wil::make_event_watcher_nothrow(wistd::move(eventToPass), []{});
|
||||
REQUIRE(watcher != nullptr);
|
||||
REQUIRE(eventToPass.get() == nullptr); // move construction must take it
|
||||
}
|
||||
|
||||
SECTION("Create unique_event_watcher_nothrow with handle")
|
||||
{
|
||||
wil::unique_event_nothrow eventToDupe;
|
||||
FAIL_FAST_IF_FAILED(eventToDupe.create(wil::EventOptions::None));
|
||||
auto watcher = wil::make_event_watcher_nothrow(eventToDupe.get(), []{});
|
||||
REQUIRE(watcher != nullptr);
|
||||
REQUIRE(eventToDupe.get() != nullptr); // handle duped in this case
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
SECTION("Create unique_event_watcher_nothrow with unique_event")
|
||||
{
|
||||
wil::unique_event eventToPass(wil::EventOptions::None);
|
||||
auto watcher = wil::make_event_watcher_nothrow(wistd::move(eventToPass), []{});
|
||||
REQUIRE(watcher != nullptr);
|
||||
REQUIRE(eventToPass.get() == nullptr); // move construction must take it
|
||||
}
|
||||
|
||||
SECTION("Create unique_event_watcher without event")
|
||||
{
|
||||
auto watcher = wil::make_event_watcher([]{});
|
||||
}
|
||||
|
||||
SECTION("Create unique_event_watcher with unique_event_nothrow")
|
||||
{
|
||||
wil::unique_event_nothrow eventToPass;
|
||||
THROW_IF_FAILED(eventToPass.create(wil::EventOptions::None));
|
||||
auto watcher = wil::make_event_watcher(wistd::move(eventToPass), []{});
|
||||
REQUIRE(eventToPass.get() == nullptr); // move construction must take it
|
||||
}
|
||||
|
||||
SECTION("Create unique_event_watcher with unique_event")
|
||||
{
|
||||
wil::unique_event eventToPass(wil::EventOptions::None);
|
||||
auto watcher = wil::make_event_watcher(wistd::move(eventToPass), []{});
|
||||
REQUIRE(eventToPass.get() == nullptr); // move construction must take it
|
||||
}
|
||||
|
||||
SECTION("Create unique_event_watcher with handle")
|
||||
{
|
||||
wil::unique_event eventToDupe(wil::EventOptions::None);
|
||||
auto watcher = wil::make_event_watcher(eventToDupe.get(), []{});
|
||||
REQUIRE(eventToDupe.get() != nullptr); // handle duped in this case
|
||||
}
|
||||
|
||||
SECTION("Create unique_event_watcher shared watcher")
|
||||
{
|
||||
wil::shared_event_watcher sharedWatcher = wil::make_event_watcher([]{});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static auto make_event(wil::EventOptions options = wil::EventOptions::None)
|
||||
{
|
||||
wil::unique_event_nothrow result;
|
||||
FAIL_FAST_IF_FAILED(result.create(options));
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_CASE("EventWatcherTests::VerifyDelivery", "[resource][event_watcher]")
|
||||
{
|
||||
auto notificationReceived = make_event();
|
||||
|
||||
int volatile countObserved = 0;
|
||||
auto watcher = wil::make_event_watcher_nothrow([&]
|
||||
{
|
||||
countObserved++;
|
||||
notificationReceived.SetEvent();
|
||||
});
|
||||
REQUIRE(watcher != nullptr);
|
||||
|
||||
watcher.SetEvent();
|
||||
REQUIRE(notificationReceived.wait(5000)); // 5 second max wait
|
||||
|
||||
watcher.SetEvent();
|
||||
REQUIRE(notificationReceived.wait(5000)); // 5 second max wait
|
||||
REQUIRE(countObserved == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("EventWatcherTests::VerifyLastChangeObserved", "[resource][event_watcher]")
|
||||
{
|
||||
wil::EventOptions const eventOptions[] =
|
||||
{
|
||||
wil::EventOptions::None,
|
||||
wil::EventOptions::ManualReset,
|
||||
wil::EventOptions::Signaled,
|
||||
wil::EventOptions::ManualReset | wil::EventOptions::Signaled,
|
||||
};
|
||||
|
||||
for (auto const &eventOption : eventOptions)
|
||||
{
|
||||
auto allChangesMade = make_event(wil::EventOptions::ManualReset); // ManualReset to avoid hang in case where 2 callbacks are generated (a test failure).
|
||||
auto processedChange = make_event();
|
||||
|
||||
DWORD volatile stateToObserve = 0;
|
||||
DWORD volatile lastObservedState = 0;
|
||||
int volatile countObserved = 0;
|
||||
auto watcher = wil::make_event_watcher_nothrow(make_event(eventOption), [&]
|
||||
{
|
||||
allChangesMade.wait();
|
||||
countObserved++;
|
||||
lastObservedState = stateToObserve;
|
||||
processedChange.SetEvent();
|
||||
});
|
||||
REQUIRE(watcher != nullptr);
|
||||
|
||||
stateToObserve = 1;
|
||||
watcher.SetEvent();
|
||||
stateToObserve = 2;
|
||||
watcher.SetEvent();
|
||||
|
||||
allChangesMade.SetEvent();
|
||||
REQUIRE(processedChange.wait(5000));
|
||||
|
||||
REQUIRE((countObserved == 1 || countObserved == 2)); // ensure the race worked how we wanted it to
|
||||
REQUIRE(lastObservedState == stateToObserve);
|
||||
}
|
||||
}
|
||||
|
||||
#define ROOT_KEY_PAIR HKEY_CURRENT_USER, L"Software\\Microsoft\\RegistryWatcherTest"
|
||||
|
||||
TEST_CASE("RegistryWatcherTests::Construction", "[registry][registry_watcher]")
|
||||
{
|
||||
SECTION("Create unique_registry_watcher_nothrow with string")
|
||||
{
|
||||
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind){});
|
||||
REQUIRE(watcher);
|
||||
}
|
||||
|
||||
SECTION("Create unique_registry_watcher_nothrow with unique_hkey")
|
||||
{
|
||||
wil::unique_hkey keyToMove;
|
||||
REQUIRE_SUCCEEDED(HRESULT_FROM_WIN32(RegCreateKeyExW(ROOT_KEY_PAIR, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToMove, nullptr)));
|
||||
|
||||
auto watcher = wil::make_registry_watcher_nothrow(wistd::move(keyToMove), true, [&](wil::RegistryChangeKind){});
|
||||
REQUIRE(watcher);
|
||||
REQUIRE(keyToMove.get() == nullptr); // ownership is transferred
|
||||
}
|
||||
|
||||
SECTION("Create unique_registry_watcher_nothrow with handle")
|
||||
{
|
||||
// construct with just an open registry key
|
||||
wil::unique_hkey rootKey;
|
||||
REQUIRE_SUCCEEDED(HRESULT_FROM_WIN32(RegCreateKeyExW(ROOT_KEY_PAIR, 0, nullptr, 0, KEY_NOTIFY, nullptr, &rootKey, nullptr)));
|
||||
|
||||
auto watcher = wil::make_registry_watcher_nothrow(rootKey.get(), L"", true, [&](wil::RegistryChangeKind){});
|
||||
REQUIRE(watcher);
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
SECTION("Create unique_registry_watcher with string")
|
||||
{
|
||||
REQUIRE_NOTHROW(wil::make_registry_watcher(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind){}));
|
||||
}
|
||||
|
||||
SECTION("Create unique_registry_watcher with unique_hkey")
|
||||
{
|
||||
wil::unique_hkey keyToMove;
|
||||
THROW_IF_FAILED(HRESULT_FROM_WIN32(RegCreateKeyExW(ROOT_KEY_PAIR, 0, nullptr, 0, KEY_NOTIFY, nullptr, &keyToMove, nullptr)));
|
||||
|
||||
REQUIRE_NOTHROW(wil::make_registry_watcher(wistd::move(keyToMove), true, [&](wil::RegistryChangeKind){}));
|
||||
REQUIRE(keyToMove.get() == nullptr); // ownership is transferred
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void SetRegistryValue(
|
||||
_In_ HKEY hKey,
|
||||
_In_opt_ LPCWSTR lpSubKey,
|
||||
_In_opt_ LPCWSTR lpValueName,
|
||||
_In_ DWORD dwType,
|
||||
_In_reads_bytes_opt_(cbData) LPCVOID lpData,
|
||||
_In_ DWORD cbData)
|
||||
{
|
||||
wil::unique_hkey key;
|
||||
REQUIRE(RegOpenKeyExW(hKey, lpSubKey, 0, KEY_WRITE, &key) == ERROR_SUCCESS);
|
||||
REQUIRE(RegSetValueExW(key.get(), lpValueName, 0, dwType, static_cast<BYTE const*>(lpData), cbData) == ERROR_SUCCESS);
|
||||
}
|
||||
|
||||
TEST_CASE("RegistryWatcherTests::VerifyDelivery", "[registry][registry_watcher]")
|
||||
{
|
||||
RegDeleteTreeW(ROOT_KEY_PAIR); // So that we get the 'Modify' event
|
||||
auto notificationReceived = make_event();
|
||||
|
||||
int volatile countObserved = 0;
|
||||
auto volatile observedChangeType = wil::RegistryChangeKind::Delete;
|
||||
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
|
||||
{
|
||||
countObserved++;
|
||||
observedChangeType = changeType;
|
||||
notificationReceived.SetEvent();
|
||||
});
|
||||
REQUIRE(watcher);
|
||||
|
||||
DWORD value = 1;
|
||||
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
|
||||
REQUIRE(notificationReceived.wait(5000));
|
||||
REQUIRE(observedChangeType == wil::RegistryChangeKind::Modify);
|
||||
|
||||
value++;
|
||||
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
|
||||
REQUIRE(notificationReceived.wait(5000));
|
||||
REQUIRE(countObserved == 2);
|
||||
REQUIRE(observedChangeType == wil::RegistryChangeKind::Modify);
|
||||
}
|
||||
|
||||
TEST_CASE("RegistryWatcherTests::VerifyLastChangeObserved", "[registry][registry_watcher]")
|
||||
{
|
||||
RegDeleteTreeW(ROOT_KEY_PAIR);
|
||||
auto allChangesMade = make_event(wil::EventOptions::ManualReset); // ManualReset for the case where both registry operations result in a callback.
|
||||
auto processedChange = make_event();
|
||||
|
||||
DWORD volatile stateToObserve = 0;
|
||||
DWORD volatile lastObservedState = 0;
|
||||
DWORD volatile lastObservedValue = 0;
|
||||
int volatile countObserved = 0;
|
||||
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&, called = false](wil::RegistryChangeKind) mutable
|
||||
{
|
||||
// This callback may be called more than once (since we modify the key twice), but we're holding references to
|
||||
// local variables. Therefore, bail out if this is not the first time we're called
|
||||
if (called)
|
||||
{
|
||||
return;
|
||||
}
|
||||
called = true;
|
||||
|
||||
allChangesMade.wait();
|
||||
countObserved++;
|
||||
lastObservedState = stateToObserve;
|
||||
DWORD value, cbValue = sizeof(value);
|
||||
RegGetValueW(ROOT_KEY_PAIR, L"value", RRF_RT_REG_DWORD, nullptr, &value, &cbValue);
|
||||
lastObservedValue = value;
|
||||
processedChange.SetEvent();
|
||||
});
|
||||
REQUIRE(watcher);
|
||||
|
||||
DWORD value;
|
||||
// make 2 changes and verify that only the last gets observed
|
||||
stateToObserve = 1;
|
||||
value = 0;
|
||||
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
|
||||
|
||||
stateToObserve = 2;
|
||||
value = 1;
|
||||
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
|
||||
|
||||
allChangesMade.SetEvent();
|
||||
REQUIRE(processedChange.wait(5000));
|
||||
|
||||
REQUIRE(countObserved >= 1); // Sometimes 2 events are observed, see if this can be eliminated.
|
||||
REQUIRE(lastObservedState == stateToObserve);
|
||||
REQUIRE(lastObservedValue == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("RegistryWatcherTests::VerifyDeleteBehavior", "[registry][registry_watcher]")
|
||||
{
|
||||
auto notificationReceived = make_event();
|
||||
|
||||
int volatile countObserved = 0;
|
||||
auto volatile observedChangeType = wil::RegistryChangeKind::Modify;
|
||||
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
|
||||
{
|
||||
countObserved++;
|
||||
observedChangeType = changeType;
|
||||
notificationReceived.SetEvent();
|
||||
});
|
||||
REQUIRE(watcher);
|
||||
|
||||
RegDeleteTreeW(ROOT_KEY_PAIR); // delete the key to signal the watcher with the special error case
|
||||
REQUIRE(notificationReceived.wait(5000));
|
||||
REQUIRE(countObserved == 1);
|
||||
REQUIRE(observedChangeType == wil::RegistryChangeKind::Delete);
|
||||
}
|
||||
|
||||
TEST_CASE("RegistryWatcherTests::VerifyResetInCallback", "[registry][registry_watcher]")
|
||||
{
|
||||
auto notificationReceived = make_event();
|
||||
|
||||
wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, TRUE, [&](wil::RegistryChangeKind)
|
||||
{
|
||||
watcher.reset();
|
||||
DWORD value = 2;
|
||||
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
|
||||
notificationReceived.SetEvent();
|
||||
});
|
||||
REQUIRE(watcher);
|
||||
|
||||
DWORD value = 1;
|
||||
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
|
||||
REQUIRE(notificationReceived.wait(5000));
|
||||
}
|
||||
|
||||
// Stress test, disabled by default
|
||||
TEST_CASE("RegistryWatcherTests::VerifyResetInCallbackStress", "[!hide][registry][registry_watcher][stress]")
|
||||
{
|
||||
for (DWORD value = 0; value < 10000; ++value)
|
||||
{
|
||||
wil::srwlock lock;
|
||||
auto notificationReceived = make_event();
|
||||
|
||||
wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, TRUE, [&](wil::RegistryChangeKind)
|
||||
{
|
||||
{
|
||||
auto al = lock.lock_exclusive();
|
||||
watcher.reset(); // get m_refCount to 1 to ensure the Release happens on the background thread
|
||||
}
|
||||
++value;
|
||||
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
|
||||
notificationReceived.SetEvent();
|
||||
});
|
||||
REQUIRE(watcher);
|
||||
|
||||
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
|
||||
notificationReceived.wait();
|
||||
|
||||
{
|
||||
auto al = lock.lock_exclusive();
|
||||
watcher.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("RegistryWatcherTests::VerifyResetAfterDelete", "[registry][registry_watcher]")
|
||||
{
|
||||
auto notificationReceived = make_event();
|
||||
|
||||
int volatile countObserved = 0;
|
||||
auto volatile observedChangeType = wil::RegistryChangeKind::Modify;
|
||||
wil::unique_registry_watcher_nothrow watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
|
||||
{
|
||||
countObserved++;
|
||||
observedChangeType = changeType;
|
||||
notificationReceived.SetEvent();
|
||||
watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind changeType)
|
||||
{
|
||||
countObserved++;
|
||||
observedChangeType = changeType;
|
||||
notificationReceived.SetEvent();
|
||||
});
|
||||
REQUIRE(watcher);
|
||||
});
|
||||
REQUIRE(watcher);
|
||||
|
||||
RegDeleteTreeW(ROOT_KEY_PAIR); // delete the key to signal the watcher with the special error case
|
||||
notificationReceived.wait();
|
||||
REQUIRE(countObserved == 1);
|
||||
REQUIRE(observedChangeType == wil::RegistryChangeKind::Delete);
|
||||
|
||||
// wait for the reset to finish. The constructor creates the registry key
|
||||
notificationReceived.wait(300);
|
||||
DWORD value = 1;
|
||||
SetRegistryValue(ROOT_KEY_PAIR, L"value", REG_DWORD, &value, sizeof(value));
|
||||
|
||||
notificationReceived.wait();
|
||||
REQUIRE(countObserved == 2);
|
||||
REQUIRE(observedChangeType == wil::RegistryChangeKind::Modify);
|
||||
}
|
||||
|
||||
TEST_CASE("RegistryWatcherTests::VerifyCallbackFinishesBeforeFreed", "[registry][registry_watcher]")
|
||||
{
|
||||
auto notificationReceived = make_event();
|
||||
auto deleteNotification = make_event();
|
||||
|
||||
int volatile deleteObserved = 0;
|
||||
auto watcher = wil::make_registry_watcher_nothrow(ROOT_KEY_PAIR, true, [&](wil::RegistryChangeKind)
|
||||
{
|
||||
notificationReceived.SetEvent();
|
||||
// ensure that the callback is still being executed while the watcher is reset().
|
||||
deleteNotification.wait(200);
|
||||
deleteObserved++;
|
||||
notificationReceived.SetEvent();
|
||||
});
|
||||
|
||||
RegDeleteTreeW(ROOT_KEY_PAIR); // delete the key to signal the watcher with the special error case
|
||||
REQUIRE(notificationReceived.wait(5000));
|
||||
|
||||
watcher.reset();
|
||||
deleteNotification.SetEvent();
|
||||
REQUIRE(notificationReceived.wait(5000));
|
||||
REQUIRE(deleteObserved == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("FileSystemWatcherTests::Construction", "[resource][folder_watcher]")
|
||||
{
|
||||
SECTION("Create unique_folder_watcher_nothrow with valid path")
|
||||
{
|
||||
auto watcher = wil::make_folder_watcher_nothrow(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, []{});
|
||||
REQUIRE(watcher);
|
||||
}
|
||||
|
||||
SECTION("Create unique_folder_watcher_nothrow with invalid path")
|
||||
{
|
||||
auto watcher = wil::make_folder_watcher_nothrow(L"X:\\invalid path", true, wil::FolderChangeEvents::All, []{});
|
||||
REQUIRE(!watcher);
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
SECTION("Create unique_folder_watcher with valid path")
|
||||
{
|
||||
REQUIRE_NOTHROW(wil::make_folder_watcher(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, []{}));
|
||||
}
|
||||
|
||||
SECTION("Create unique_folder_watcher with invalid path")
|
||||
{
|
||||
REQUIRE_THROWS(wil::make_folder_watcher(L"X:\\invalid path", true, wil::FolderChangeEvents::All, []{}));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("FileSystemWatcherTests::VerifyDelivery", "[resource][folder_watcher]")
|
||||
{
|
||||
witest::TestFolder folder;
|
||||
REQUIRE(folder);
|
||||
|
||||
auto notificationEvent = make_event();
|
||||
int observedCount = 0;
|
||||
auto watcher = wil::make_folder_watcher_nothrow(folder.Path(), true, wil::FolderChangeEvents::All, [&]
|
||||
{
|
||||
++observedCount;
|
||||
notificationEvent.SetEvent();
|
||||
});
|
||||
REQUIRE(watcher);
|
||||
|
||||
witest::TestFile file(folder.Path(), L"file.txt");
|
||||
REQUIRE(file);
|
||||
REQUIRE(notificationEvent.wait(5000));
|
||||
REQUIRE(observedCount == 1);
|
||||
|
||||
witest::TestFile file2(folder.Path(), L"file2.txt");
|
||||
REQUIRE(file2);
|
||||
REQUIRE(notificationEvent.wait(5000));
|
||||
REQUIRE(observedCount == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("FolderChangeReaderTests::Construction", "[resource][folder_change_reader]")
|
||||
{
|
||||
SECTION("Create folder_change_reader_nothrow with valid path")
|
||||
{
|
||||
auto reader = wil::make_folder_change_reader_nothrow(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, [](auto, auto) {});
|
||||
REQUIRE(reader);
|
||||
}
|
||||
|
||||
SECTION("Create folder_change_reader_nothrow with invalid path")
|
||||
{
|
||||
auto reader = wil::make_folder_change_reader_nothrow(L"X:\\invalid path", true, wil::FolderChangeEvents::All, [](auto, auto) {});
|
||||
REQUIRE(!reader);
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
SECTION("Create folder_change_reader with valid path")
|
||||
{
|
||||
REQUIRE_NOTHROW(wil::make_folder_change_reader(L"C:\\Windows\\System32", true, wil::FolderChangeEvents::All, [](auto, auto) {}));
|
||||
}
|
||||
|
||||
SECTION("Create folder_change_reader with invalid path")
|
||||
{
|
||||
REQUIRE_THROWS(wil::make_folder_change_reader(L"X:\\invalid path", true, wil::FolderChangeEvents::All, [](auto, auto) {}));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("FolderChangeReaderTests::VerifyDelivery", "[resource][folder_change_reader]")
|
||||
{
|
||||
witest::TestFolder folder;
|
||||
REQUIRE(folder);
|
||||
|
||||
auto notificationEvent = make_event();
|
||||
wil::FolderChangeEvent observedEvent;
|
||||
wchar_t observedFileName[MAX_PATH] = L"";
|
||||
auto reader = wil::make_folder_change_reader_nothrow(folder.Path(), true, wil::FolderChangeEvents::All,
|
||||
[&](wil::FolderChangeEvent event, PCWSTR fileName)
|
||||
{
|
||||
observedEvent = event;
|
||||
StringCchCopyW(observedFileName, ARRAYSIZE(observedFileName), fileName);
|
||||
notificationEvent.SetEvent();
|
||||
});
|
||||
REQUIRE(reader);
|
||||
|
||||
witest::TestFile testFile(folder.Path(), L"file.txt");
|
||||
REQUIRE(testFile);
|
||||
REQUIRE(notificationEvent.wait(5000));
|
||||
REQUIRE(observedEvent == wil::FolderChangeEvent::Added);
|
||||
REQUIRE(wcscmp(observedFileName, L"file.txt") == 0);
|
||||
|
||||
witest::TestFile testFile2(folder.Path(), L"file2.txt");
|
||||
REQUIRE(testFile2);
|
||||
REQUIRE(notificationEvent.wait(5000));
|
||||
REQUIRE(observedEvent == wil::FolderChangeEvent::Added);
|
||||
REQUIRE(wcscmp(observedFileName, L"file2.txt") == 0);
|
||||
}
|
|
@ -0,0 +1,842 @@
|
|||
|
||||
#include <time.h>
|
||||
#include <wil/winrt.h>
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
#include <map>
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "FakeWinRTTypes.h"
|
||||
#include "test_objects.h"
|
||||
|
||||
using namespace ABI::Windows::Foundation;
|
||||
using namespace ABI::Windows::Foundation::Collections;
|
||||
using namespace ABI::Windows::Storage;
|
||||
using namespace ABI::Windows::System;
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
|
||||
TEST_CASE("WinRTTests::VerifyTraitsTypes", "[winrt]")
|
||||
{
|
||||
static_assert(wistd::is_same_v<bool, typename wil::details::LastType<int, bool>::type>, "");
|
||||
static_assert(wistd::is_same_v<int, typename wil::details::LastType<int>::type>, "");
|
||||
|
||||
static_assert(wistd::is_same_v<IAsyncAction*, decltype(wil::details::GetReturnParamPointerType(&IFileIOStatics::WriteTextAsync))>, "");
|
||||
static_assert(wistd::is_same_v<IAsyncOperation<bool>*, decltype(wil::details::GetReturnParamPointerType(&ILauncherStatics::LaunchUriAsync))>, "");
|
||||
|
||||
static_assert(wistd::is_same_v<void, decltype(wil::details::GetAsyncResultType(static_cast<IAsyncAction*>(nullptr)))>, "");
|
||||
static_assert(wistd::is_same_v<boolean, decltype(wil::details::GetAsyncResultType(static_cast<IAsyncOperation<bool>*>(nullptr)))>, "");
|
||||
static_assert(wistd::is_same_v<IStorageFile*, decltype(wil::details::GetAsyncResultType(static_cast<IAsyncOperation<StorageFile*>*>(nullptr)))>, "");
|
||||
}
|
||||
|
||||
template <bool InhibitArrayReferences, bool IgnoreCase, typename LhsT, typename RhsT>
|
||||
void DoHStringComparisonTest(LhsT&& lhs, RhsT&& rhs, int relation)
|
||||
{
|
||||
using compare = wil::details::hstring_compare<InhibitArrayReferences, IgnoreCase>;
|
||||
|
||||
// == and !=
|
||||
REQUIRE(compare::equals(lhs, rhs) == (relation == 0));
|
||||
REQUIRE(compare::not_equals(lhs, rhs) == (relation != 0));
|
||||
|
||||
REQUIRE(compare::equals(rhs, lhs) == (relation == 0));
|
||||
REQUIRE(compare::not_equals(rhs, lhs) == (relation != 0));
|
||||
|
||||
// < and >=
|
||||
REQUIRE(compare::less(lhs, rhs) == (relation < 0));
|
||||
REQUIRE(compare::greater_equals(lhs, rhs) == (relation >= 0));
|
||||
|
||||
REQUIRE(compare::less(rhs, lhs) == (relation > 0));
|
||||
REQUIRE(compare::greater_equals(rhs, lhs) == (relation <= 0));
|
||||
|
||||
// > and <=
|
||||
REQUIRE(compare::greater(lhs, rhs) == (relation > 0));
|
||||
REQUIRE(compare::less_equals(lhs, rhs) == (relation <= 0));
|
||||
|
||||
REQUIRE(compare::greater(rhs, lhs) == (relation < 0));
|
||||
REQUIRE(compare::less_equals(rhs, lhs) == (relation >= 0));
|
||||
|
||||
// We wish to test with both const and non-const values. We can do this for free here so long as the type is
|
||||
// not an array since changing the const-ness of an array may change the expected results
|
||||
#pragma warning(suppress: 4127)
|
||||
if (!wistd::is_array<wistd::remove_reference_t<LhsT>>::value &&
|
||||
!wistd::is_const<wistd::remove_reference_t<LhsT>>::value)
|
||||
{
|
||||
const wistd::remove_reference_t<LhsT>& constLhs = lhs;
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(constLhs, rhs, relation);
|
||||
}
|
||||
|
||||
#pragma warning(suppress: 4127)
|
||||
if (!wistd::is_array<wistd::remove_reference_t<RhsT>>::value &&
|
||||
!wistd::is_const<wistd::remove_reference_t<RhsT>>::value)
|
||||
{
|
||||
const wistd::remove_reference_t<RhsT>& constRhs = rhs;
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, constRhs, relation);
|
||||
}
|
||||
}
|
||||
|
||||
// The two string arguments are expected to compare equal to one another using the specified IgnoreCase argument and
|
||||
// contain at least one embedded null character
|
||||
template <bool InhibitArrayReferences, bool IgnoreCase, size_t Size>
|
||||
void DoHStringSameValueComparisonTest(const wchar_t (&lhs)[Size], const wchar_t (&rhs)[Size])
|
||||
{
|
||||
wchar_t lhsNonConstArray[Size + 5];
|
||||
wchar_t rhsNonConstArray[Size + 5];
|
||||
wcsncpy_s(lhsNonConstArray, lhs, Size);
|
||||
wcsncpy_s(rhsNonConstArray, rhs, Size);
|
||||
|
||||
// For non-const arrays, we should never deduce length, so even though we append different values to each string, we
|
||||
// do so after the last null character, so they should never be read
|
||||
wcsncpy_s(lhsNonConstArray + Size + 1, 4, L"foo", 3);
|
||||
wcsncpy_s(rhsNonConstArray + Size + 1, 4, L"bar", 3);
|
||||
|
||||
const wchar_t* lhsCstr = lhs;
|
||||
const wchar_t* rhsCstr = rhs;
|
||||
|
||||
HStringReference lhsRef(lhs);
|
||||
HStringReference rhsRef(rhs);
|
||||
HString lhsStr;
|
||||
HString rhsStr;
|
||||
REQUIRE_SUCCEEDED(lhsStr.Set(lhs));
|
||||
REQUIRE_SUCCEEDED(rhsStr.Set(rhs));
|
||||
auto lhsHstr = lhsStr.Get();
|
||||
auto rhsHstr = rhsStr.Get();
|
||||
|
||||
wil::unique_hstring lhsUniqueStr;
|
||||
wil::unique_hstring rhsUniqueStr;
|
||||
REQUIRE_SUCCEEDED(lhsStr.CopyTo(&lhsUniqueStr));
|
||||
REQUIRE_SUCCEEDED(rhsStr.CopyTo(&rhsUniqueStr));
|
||||
|
||||
// Const array - embedded nulls are included only if InhibitArrayReferences is false
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhs, 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsNonConstArray, InhibitArrayReferences ? 0 : 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsCstr, InhibitArrayReferences ? 0 : 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsRef, InhibitArrayReferences ? -1 : 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsStr, InhibitArrayReferences ? -1 : 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsHstr, InhibitArrayReferences ? -1 : 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsUniqueStr, InhibitArrayReferences ? -1 : 0);
|
||||
|
||||
// Non-const array - *never* deduce length
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsNonConstArray, 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsCstr, 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsRef, -1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsStr, -1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsHstr, -1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsUniqueStr, -1);
|
||||
|
||||
// C string - impossible to deduce length
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsCstr, 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsRef, -1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsStr, -1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsHstr, -1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsUniqueStr, -1);
|
||||
|
||||
// HStringReference
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsRef, 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsStr, 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsHstr, 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsUniqueStr, 0);
|
||||
|
||||
// HString
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsStr, 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsHstr, 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsUniqueStr, 0);
|
||||
|
||||
// Raw HSTRING
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsHstr, rhsHstr, 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsHstr, rhsUniqueStr, 0);
|
||||
|
||||
// wil::unique_hstring
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsUniqueStr, rhsUniqueStr, 0);
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
std::wstring lhsWstr(lhs, 7);
|
||||
std::wstring rhsWstr(rhs, 7);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsWstr, 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhs, InhibitArrayReferences ? 1 : 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsNonConstArray, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsCstr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsRef, 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsStr, 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsHstr, 0);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsUniqueStr, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
// It's expected that the first argument (lhs) compares greater than the second argument (rhs)
|
||||
template <bool InhibitArrayReferences, bool IgnoreCase, size_t LhsSize, size_t RhsSize>
|
||||
void DoHStringDifferentValueComparisonTest(const wchar_t (&lhs)[LhsSize], const wchar_t (&rhs)[RhsSize])
|
||||
{
|
||||
wchar_t lhsNonConstArray[LhsSize];
|
||||
wchar_t rhsNonConstArray[RhsSize];
|
||||
wcsncpy_s(lhsNonConstArray, lhs, LhsSize);
|
||||
wcsncpy_s(rhsNonConstArray, rhs, RhsSize);
|
||||
|
||||
const wchar_t* lhsCstr = lhs;
|
||||
const wchar_t* rhsCstr = rhs;
|
||||
|
||||
HStringReference lhsRef(lhs);
|
||||
HStringReference rhsRef(rhs);
|
||||
HString lhsStr;
|
||||
HString rhsStr;
|
||||
REQUIRE_SUCCEEDED(lhsStr.Set(lhs));
|
||||
REQUIRE_SUCCEEDED(rhsStr.Set(rhs));
|
||||
auto lhsHstr = lhsStr.Get();
|
||||
auto rhsHstr = rhsStr.Get();
|
||||
|
||||
wil::unique_hstring lhsUniqueStr;
|
||||
wil::unique_hstring rhsUniqueStr;
|
||||
REQUIRE_SUCCEEDED(lhsStr.CopyTo(&lhsUniqueStr));
|
||||
REQUIRE_SUCCEEDED(rhsStr.CopyTo(&rhsUniqueStr));
|
||||
|
||||
// Const array
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhs, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsNonConstArray, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsCstr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsRef, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsStr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsHstr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhs, rhsUniqueStr, 1);
|
||||
|
||||
// Non-const array
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsNonConstArray, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsCstr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsRef, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsStr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsHstr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsNonConstArray, rhsUniqueStr, 1);
|
||||
|
||||
// C string
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsCstr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsRef, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsStr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsHstr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsCstr, rhsUniqueStr, 1);
|
||||
|
||||
// HStringReference
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsRef, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsStr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsHstr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsRef, rhsUniqueStr, 1);
|
||||
|
||||
// HString
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsStr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsHstr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsStr, rhsUniqueStr, 1);
|
||||
|
||||
// Raw HSTRING
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsHstr, rhsHstr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsHstr, rhsUniqueStr, 1);
|
||||
|
||||
// wil::unique_hstring
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsUniqueStr, rhsUniqueStr, 1);
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
std::wstring lhsWstr(lhs, 7);
|
||||
std::wstring rhsWstr(rhs, 7);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsWstr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhs, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsNonConstArray, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsCstr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsRef, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsStr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsHstr, 1);
|
||||
DoHStringComparisonTest<InhibitArrayReferences, IgnoreCase>(lhsWstr, rhsUniqueStr, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("WinRTTests::HStringComparison", "[winrt][hstring_compare]")
|
||||
{
|
||||
SECTION("Don't inhibit arrays")
|
||||
{
|
||||
DoHStringSameValueComparisonTest<false, false>(L"foo\0bar", L"foo\0bar");
|
||||
DoHStringDifferentValueComparisonTest<false, false>(L"foo", L"bar");
|
||||
}
|
||||
|
||||
SECTION("Inhibit arrays")
|
||||
{
|
||||
DoHStringSameValueComparisonTest<true, false>(L"foo\0bar", L"foo\0bar");
|
||||
DoHStringDifferentValueComparisonTest<true, false>(L"foo", L"bar");
|
||||
}
|
||||
|
||||
SECTION("Ignore case")
|
||||
{
|
||||
DoHStringSameValueComparisonTest<true, true>(L"foo\0bar", L"FoO\0bAR");
|
||||
DoHStringDifferentValueComparisonTest<true, true>(L"Foo", L"baR");
|
||||
}
|
||||
|
||||
SECTION("Empty string")
|
||||
{
|
||||
const wchar_t constArray[] = L"";
|
||||
wchar_t nonConstArray[] = L"";
|
||||
const wchar_t* cstr = constArray;
|
||||
const wchar_t* nullCstr = nullptr;
|
||||
|
||||
// str may end up referencing a null HSTRING. That's fine; we'll just test null HSTRING twice
|
||||
HString str;
|
||||
REQUIRE_SUCCEEDED(str.Set(constArray));
|
||||
HSTRING nullHstr = nullptr;
|
||||
|
||||
// Const array - impossible to use null value
|
||||
DoHStringComparisonTest<false, false>(constArray, constArray, 0);
|
||||
DoHStringComparisonTest<false, false>(constArray, nonConstArray, 0);
|
||||
DoHStringComparisonTest<false, false>(constArray, cstr, 0);
|
||||
DoHStringComparisonTest<false, false>(constArray, nullCstr, 0);
|
||||
DoHStringComparisonTest<false, false>(constArray, str.Get(), 0);
|
||||
DoHStringComparisonTest<false, false>(constArray, nullHstr, 0);
|
||||
|
||||
// Non-const array - impossible to use null value
|
||||
DoHStringComparisonTest<false, false>(nonConstArray, nonConstArray, 0);
|
||||
DoHStringComparisonTest<false, false>(nonConstArray, cstr, 0);
|
||||
DoHStringComparisonTest<false, false>(nonConstArray, nullCstr, 0);
|
||||
DoHStringComparisonTest<false, false>(nonConstArray, str.Get(), 0);
|
||||
DoHStringComparisonTest<false, false>(nonConstArray, nullHstr, 0);
|
||||
|
||||
// Non-null c-string
|
||||
DoHStringComparisonTest<false, false>(cstr, cstr, 0);
|
||||
DoHStringComparisonTest<false, false>(cstr, nullCstr, 0);
|
||||
DoHStringComparisonTest<false, false>(cstr, str.Get(), 0);
|
||||
DoHStringComparisonTest<false, false>(cstr, nullHstr, 0);
|
||||
|
||||
// Null c-string
|
||||
DoHStringComparisonTest<false, false>(nullCstr, nullCstr, 0);
|
||||
DoHStringComparisonTest<false, false>(nullCstr, str.Get(), 0);
|
||||
DoHStringComparisonTest<false, false>(nullCstr, nullHstr, 0);
|
||||
|
||||
// (Possibly) non-null HSTRING
|
||||
DoHStringComparisonTest<false, false>(str.Get(), str.Get(), 0);
|
||||
DoHStringComparisonTest<false, false>(str.Get(), nullHstr, 0);
|
||||
|
||||
// Null HSTRING
|
||||
DoHStringComparisonTest<false, false>(nullHstr, nullHstr, 0);
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
std::wstring wstr;
|
||||
DoHStringComparisonTest<false, false>(wstr, wstr, 0);
|
||||
DoHStringComparisonTest<false, false>(wstr, constArray, 0);
|
||||
DoHStringComparisonTest<false, false>(wstr, nonConstArray, 0);
|
||||
DoHStringComparisonTest<false, false>(wstr, cstr, 0);
|
||||
DoHStringComparisonTest<false, false>(wstr, nullCstr, 0);
|
||||
DoHStringComparisonTest<false, false>(wstr, str.Get(), 0);
|
||||
DoHStringComparisonTest<false, false>(wstr, nullHstr, 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
TEST_CASE("WinRTTests::HStringMapTest", "[winrt][hstring_compare]")
|
||||
{
|
||||
int nextValue = 0;
|
||||
std::map<std::wstring, int> wstringMap;
|
||||
wstringMap.emplace(L"foo", nextValue++);
|
||||
wstringMap.emplace(L"bar", nextValue++);
|
||||
wstringMap.emplace(std::wstring(L"foo\0bar", 7), nextValue++);
|
||||
wstringMap.emplace(L"adding", nextValue++);
|
||||
wstringMap.emplace(L"quite", nextValue++);
|
||||
wstringMap.emplace(L"a", nextValue++);
|
||||
wstringMap.emplace(L"few", nextValue++);
|
||||
wstringMap.emplace(L"more", nextValue++);
|
||||
wstringMap.emplace(L"values", nextValue++);
|
||||
wstringMap.emplace(L"for", nextValue++);
|
||||
wstringMap.emplace(L"testing", nextValue++);
|
||||
wstringMap.emplace(L"", nextValue++);
|
||||
|
||||
std::map<HString, int> hstringMap;
|
||||
for (auto& pair : wstringMap)
|
||||
{
|
||||
HString str;
|
||||
THROW_IF_FAILED(str.Set(pair.first.c_str(), static_cast<UINT>(pair.first.length())));
|
||||
hstringMap.emplace(std::move(str), pair.second);
|
||||
}
|
||||
|
||||
// Order should be the same as the map of wstring
|
||||
auto itr = hstringMap.begin();
|
||||
for (auto& pair : wstringMap)
|
||||
{
|
||||
REQUIRE(itr != hstringMap.end());
|
||||
REQUIRE(itr->first == HStringReference(pair.first.c_str(), static_cast<UINT>(pair.first.length())));
|
||||
|
||||
// Should also be able to find the value
|
||||
REQUIRE(hstringMap.find(pair.first) != hstringMap.end());
|
||||
|
||||
++itr;
|
||||
}
|
||||
REQUIRE(itr == hstringMap.end());
|
||||
|
||||
const wchar_t constArray[] = L"foo\0bar";
|
||||
wchar_t nonConstArray[] = L"foo\0bar";
|
||||
const wchar_t* cstr = constArray;
|
||||
|
||||
HString key;
|
||||
wil::unique_hstring uniqueHstr;
|
||||
THROW_IF_FAILED(key.Set(constArray));
|
||||
THROW_IF_FAILED(key.CopyTo(&uniqueHstr));
|
||||
|
||||
HStringReference ref(constArray);
|
||||
std::wstring wstr(constArray, 7);
|
||||
|
||||
auto verifyFunc = [&](int expectedValue, auto&& keyValue)
|
||||
{
|
||||
auto itr = hstringMap.find(std::forward<decltype(keyValue)>(keyValue));
|
||||
REQUIRE(itr != hstringMap.end());
|
||||
REQUIRE(expectedValue == itr->second);
|
||||
};
|
||||
|
||||
// The following should find "foo\0bar"
|
||||
auto expectedValue = wstringMap[wstr];
|
||||
verifyFunc(expectedValue, uniqueHstr);
|
||||
verifyFunc(expectedValue, key);
|
||||
verifyFunc(expectedValue, key.Get());
|
||||
verifyFunc(expectedValue, ref);
|
||||
verifyFunc(expectedValue, wstr);
|
||||
|
||||
// Arrays/strings should not deduce length and should therefore find "foo"
|
||||
expectedValue = wstringMap[L"foo"];
|
||||
verifyFunc(expectedValue, constArray);
|
||||
verifyFunc(expectedValue, nonConstArray);
|
||||
verifyFunc(expectedValue, cstr);
|
||||
|
||||
// Should not ignore case
|
||||
REQUIRE(hstringMap.find(L"FOO") == hstringMap.end());
|
||||
|
||||
// Should also be able to find empty values
|
||||
const wchar_t constEmptyArray[] = L"";
|
||||
wchar_t nonConstEmptyArray[] = L"";
|
||||
const wchar_t* emptyCstr = constEmptyArray;
|
||||
const wchar_t* nullCstr = nullptr;
|
||||
|
||||
HString emptyStr;
|
||||
HSTRING nullHstr = nullptr;
|
||||
|
||||
std::wstring emptyWstr;
|
||||
|
||||
expectedValue = wstringMap[L""];
|
||||
verifyFunc(expectedValue, constEmptyArray);
|
||||
verifyFunc(expectedValue, nonConstEmptyArray);
|
||||
verifyFunc(expectedValue, emptyCstr);
|
||||
verifyFunc(expectedValue, nullCstr);
|
||||
verifyFunc(expectedValue, emptyStr);
|
||||
verifyFunc(expectedValue, nullHstr);
|
||||
verifyFunc(expectedValue, emptyWstr);
|
||||
}
|
||||
|
||||
TEST_CASE("WinRTTests::HStringCaseInsensitiveMapTest", "[winrt][hstring_compare]")
|
||||
{
|
||||
std::map<HString, int, wil::hstring_insensitive_less> hstringMap;
|
||||
|
||||
auto emplaceFunc = [&](auto&& key, int value)
|
||||
{
|
||||
HString str;
|
||||
THROW_IF_FAILED(str.Set(std::forward<decltype(key)>(key)));
|
||||
hstringMap.emplace(std::move(str), value);
|
||||
};
|
||||
|
||||
int nextValue = 0;
|
||||
int fooValue = nextValue++;
|
||||
emplaceFunc(L"foo", fooValue);
|
||||
emplaceFunc(L"bar", nextValue++);
|
||||
int foobarValue = nextValue++;
|
||||
emplaceFunc(L"foo\0bar", foobarValue);
|
||||
emplaceFunc(L"foobar", nextValue++);
|
||||
emplaceFunc(L"adding", nextValue++);
|
||||
emplaceFunc(L"some", nextValue++);
|
||||
emplaceFunc(L"more", nextValue++);
|
||||
emplaceFunc(L"values", nextValue++);
|
||||
emplaceFunc(L"for", nextValue++);
|
||||
emplaceFunc(L"testing", nextValue++);
|
||||
WI_ASSERT(static_cast<size_t>(nextValue) == hstringMap.size());
|
||||
|
||||
const wchar_t constArray[] = L"FoO\0BAr";
|
||||
wchar_t nonConstArray[] = L"fOo\0baR";
|
||||
const wchar_t* cstr = constArray;
|
||||
|
||||
HString key;
|
||||
wil::unique_hstring uniqueHstr;
|
||||
THROW_IF_FAILED(key.Set(constArray));
|
||||
THROW_IF_FAILED(key.CopyTo(&uniqueHstr));
|
||||
|
||||
HStringReference ref(constArray);
|
||||
std::wstring wstr(constArray, 7);
|
||||
|
||||
auto verifyFunc = [&](int expectedValue, auto&& key)
|
||||
{
|
||||
auto itr = hstringMap.find(std::forward<decltype(key)>(key));
|
||||
REQUIRE(itr != std::end(hstringMap));
|
||||
REQUIRE(expectedValue == itr->second);
|
||||
};
|
||||
|
||||
// The following should find "foo\0bar"
|
||||
verifyFunc(foobarValue, uniqueHstr);
|
||||
verifyFunc(foobarValue, key);
|
||||
verifyFunc(foobarValue, key.Get());
|
||||
verifyFunc(foobarValue, ref);
|
||||
verifyFunc(foobarValue, wstr);
|
||||
|
||||
// Arrays/strings should not deduce length and should therefore find "foo"
|
||||
verifyFunc(fooValue, constArray);
|
||||
verifyFunc(fooValue, nonConstArray);
|
||||
verifyFunc(fooValue, cstr);
|
||||
}
|
||||
#endif
|
||||
|
||||
// This is not a test method, nor should it be called. This is a compilation-only test.
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
void RunWhenCompleteCompilationTest()
|
||||
{
|
||||
{
|
||||
ComPtr<IAsyncOperation<HSTRING>> stringOp;
|
||||
wil::run_when_complete(stringOp.Get(), [](HRESULT /* result */, HSTRING /* value */) {});
|
||||
auto result = wil::wait_for_completion(stringOp.Get());
|
||||
}
|
||||
|
||||
{
|
||||
ComPtr<IAsyncOperationWithProgress<HSTRING, UINT64>> stringOpWithProgress;
|
||||
wil::run_when_complete(stringOpWithProgress.Get(), [](HRESULT /* result */, HSTRING /* value */) {});
|
||||
auto result = wil::wait_for_completion(stringOpWithProgress.Get());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("WinRTTests::RunWhenCompleteMoveOnlyTest", "[winrt][run_when_complete]")
|
||||
{
|
||||
auto op = Make<FakeAsyncOperation<int>>();
|
||||
REQUIRE(op);
|
||||
|
||||
bool gotEvent = false;
|
||||
auto hr = wil::run_when_complete_nothrow(op.Get(), [&gotEvent, enforce = cannot_copy{}](HRESULT hr, int result)
|
||||
{
|
||||
(void)enforce;
|
||||
REQUIRE_SUCCEEDED(hr);
|
||||
REQUIRE(result == 42);
|
||||
gotEvent = true;
|
||||
return S_OK;
|
||||
});
|
||||
REQUIRE_SUCCEEDED(hr);
|
||||
|
||||
op->Complete(S_OK, 42);
|
||||
REQUIRE(gotEvent);
|
||||
}
|
||||
|
||||
TEST_CASE("WinRTTests::WaitForCompletionTimeout", "[winrt][wait_for_completion]")
|
||||
{
|
||||
auto op = Make<FakeAsyncOperation<bool, boolean>>();
|
||||
REQUIRE(op);
|
||||
|
||||
// The wait_for_completion* functions don't properly deduce the "decayed" async type, so force it here
|
||||
auto asyncOp = static_cast<IAsyncOperation<bool>*>(op.Get());
|
||||
|
||||
bool timedOut = false;
|
||||
REQUIRE_SUCCEEDED(wil::wait_for_completion_or_timeout_nothrow(asyncOp, 1, &timedOut));
|
||||
REQUIRE(timedOut);
|
||||
}
|
||||
|
||||
// This is not a test method, nor should it be called. This is a compilation-only test.
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4702) // Unreachable code
|
||||
void WaitForCompletionCompilationTest()
|
||||
{
|
||||
// Ensure the wait_for_completion variants compile
|
||||
FAIL_FAST_HR_MSG(E_UNEXPECTED, "This is a compilation test, and should not be called");
|
||||
|
||||
// template <typename TAsync = ABI::Windows::Foundation::IAsyncAction>
|
||||
// inline HRESULT wait_for_completion_nothrow(_In_ TAsync* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
|
||||
IAsyncAction* action = nullptr;
|
||||
wil::wait_for_completion_nothrow(action);
|
||||
wil::wait_for_completion_nothrow(action, COWAIT_DEFAULT);
|
||||
|
||||
// template <typename TResult>
|
||||
// HRESULT wait_for_completion_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperation<TResult>* operation,
|
||||
// _Out_ typename wil::details::MapAsyncOpResultType<TResult>::type* result,
|
||||
// COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
|
||||
IAsyncOperation<bool>* operation = nullptr;
|
||||
wil::wait_for_completion_nothrow(operation);
|
||||
wil::wait_for_completion_nothrow(operation, COWAIT_DEFAULT);
|
||||
|
||||
// template <typename TResult, typename TProgress>
|
||||
// HRESULT wait_for_completion_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>* operation,
|
||||
// _Out_ typename wil::details::MapAsyncOpProgressResultType<TResult, TProgress>::type* result,
|
||||
// COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
|
||||
|
||||
ComPtr<IAsyncOperation<bool>> operationWithResult;
|
||||
boolean result = false;
|
||||
wil::wait_for_completion_nothrow(operationWithResult.Get(), &result);
|
||||
wil::wait_for_completion_nothrow(operationWithResult.Get(), &result, COWAIT_DEFAULT);
|
||||
|
||||
DWORD timeoutValue = 1000; // arbitrary
|
||||
bool timedOut = false;
|
||||
|
||||
// template <typename TAsync = ABI::Windows::Foundation::IAsyncAction>
|
||||
// inline HRESULT wait_for_completion_or_timeout_nothrow(_In_ TAsync* operation,
|
||||
// DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS);
|
||||
wil::wait_for_completion_or_timeout_nothrow(action, timeoutValue, &timedOut);
|
||||
wil::wait_for_completion_or_timeout_nothrow(action, timeoutValue, &timedOut, COWAIT_DEFAULT);
|
||||
|
||||
// template <typename TResult>
|
||||
// HRESULT wait_for_completion_or_timeout_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperation<TResult>* operation,
|
||||
// _Out_ typename wil::details::MapAsyncOpResultType<TResult>::type* result,
|
||||
// DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS);
|
||||
wil::wait_for_completion_or_timeout_nothrow(operation, timeoutValue, &timedOut);
|
||||
wil::wait_for_completion_or_timeout_nothrow(operation, timeoutValue, &timedOut, COWAIT_DEFAULT);
|
||||
|
||||
// template <typename TResult, typename TProgress>
|
||||
// HRESULT wait_for_completion_or_timeout_nothrow(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>* operation,
|
||||
// _Out_ typename wil::details::MapAsyncOpProgressResultType<TResult, TProgress>::type* result,
|
||||
// DWORD timeoutValue, _Out_ bool* timedOut, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS);
|
||||
wil::wait_for_completion_or_timeout_nothrow(operationWithResult.Get(), &result, timeoutValue, &timedOut);
|
||||
wil::wait_for_completion_or_timeout_nothrow(operationWithResult.Get(), &result, timeoutValue, &timedOut, COWAIT_DEFAULT);
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
// template <typename TAsync = ABI::Windows::Foundation::IAsyncAction>
|
||||
// inline void wait_for_completion(_In_ TAsync* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
|
||||
wil::wait_for_completion(action);
|
||||
wil::wait_for_completion(action, COWAIT_DEFAULT);
|
||||
|
||||
// template <typename TResult, typename TReturn = typename wil::details::MapToSmartType<typename wil::details::MapAsyncOpResultType<TResult>::type>::type>
|
||||
// TReturn
|
||||
// wait_for_completion(_In_ ABI::Windows::Foundation::IAsyncOperation<TResult>* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
|
||||
wil::wait_for_completion(operation);
|
||||
wil::wait_for_completion(operation, COWAIT_DEFAULT);
|
||||
|
||||
// template <typename TResult, typename TProgress, typename TReturn = typename wil::details::MapToSmartType<typename wil::details::MapAsyncOpResultType<TResult>::type>::type>
|
||||
// TReturn
|
||||
// wait_for_completion(_In_ ABI::Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>* operation, COWAIT_FLAGS flags = COWAIT_DISPATCH_CALLS, DWORD timeout = INFINITE);
|
||||
result = wil::wait_for_completion(operationWithResult.Get());
|
||||
result = wil::wait_for_completion(operationWithResult.Get(), COWAIT_DEFAULT);
|
||||
#endif
|
||||
}
|
||||
#pragma warning(pop)
|
||||
|
||||
TEST_CASE("WinRTTests::TimeTTests", "[winrt][time_t]")
|
||||
{
|
||||
// Verifying that converting DateTime variable set as the date that means 0 as time_t works
|
||||
DateTime time1 = { wil::SecondsToStartOf1970 * wil::HundredNanoSecondsInSecond };
|
||||
__time64_t time_t1 = wil::DateTime_to_time_t(time1);
|
||||
REQUIRE(time_t1 == 0);
|
||||
|
||||
// Verifying that converting back to DateTime would return the same value
|
||||
DateTime time2 = wil::time_t_to_DateTime(time_t1);
|
||||
REQUIRE(time1.UniversalTime == time2.UniversalTime);
|
||||
|
||||
// Verifying that converting to time_t for non-zero value also works
|
||||
time2.UniversalTime += wil::HundredNanoSecondsInSecond * 123;
|
||||
__time64_t time_t2 = wil::DateTime_to_time_t(time2);
|
||||
REQUIRE(time_t2 - time_t1 == 123);
|
||||
|
||||
// Verifying that converting back to DateTime for non-zero value also works
|
||||
time1 = wil::time_t_to_DateTime(time_t2);
|
||||
REQUIRE(time1.UniversalTime == time2.UniversalTime);
|
||||
}
|
||||
|
||||
ComPtr<IVector<IInspectable*>> MakeSampleInspectableVector()
|
||||
{
|
||||
auto result = Make<FakeVector<IInspectable*>>();
|
||||
REQUIRE(result);
|
||||
|
||||
ComPtr<IPropertyValueStatics> propStatics;
|
||||
REQUIRE_SUCCEEDED(GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_PropertyValue).Get(), &propStatics));
|
||||
|
||||
for (UINT32 i = 0; i < 5; ++i)
|
||||
{
|
||||
ComPtr<IInspectable> myProp;
|
||||
REQUIRE_SUCCEEDED(propStatics->CreateUInt32(i, &myProp));
|
||||
REQUIRE_SUCCEEDED(result->Append(myProp.Get()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ComPtr<IVector<HSTRING>> MakeSampleStringVector()
|
||||
{
|
||||
auto result = Make<FakeVector<HSTRING>>();
|
||||
REQUIRE(result);
|
||||
|
||||
const HStringReference items[] = { HStringReference(L"one"), HStringReference(L"two"), HStringReference(L"three") };
|
||||
for (const auto& i : items)
|
||||
{
|
||||
REQUIRE_SUCCEEDED(result->Append(i.Get()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ComPtr<IVector<Point>> MakeSamplePointVector()
|
||||
{
|
||||
auto result = Make<FakeVector<Point>>();
|
||||
REQUIRE(result);
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
auto value = static_cast<float>(i);
|
||||
REQUIRE_SUCCEEDED(result->Append(Point{ value, value }));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TEST_CASE("WinRTTests::VectorRangeTest", "[winrt][vector_range]")
|
||||
{
|
||||
auto uninit = wil::RoInitialize_failfast();
|
||||
|
||||
auto inspectables = MakeSampleInspectableVector();
|
||||
unsigned count = 0;
|
||||
REQUIRE_SUCCEEDED(inspectables->get_Size(&count));
|
||||
|
||||
unsigned idx = 0;
|
||||
HRESULT success = S_OK;
|
||||
for (const auto& i : wil::get_range_nothrow(inspectables.Get(), &success))
|
||||
{
|
||||
// Duplications are not a typo - they verify the thing is callable twice
|
||||
|
||||
UINT32 value;
|
||||
ComPtr<IReference<UINT32>> intRef;
|
||||
REQUIRE_SUCCEEDED(i.CopyTo(IID_PPV_ARGS(&intRef)));
|
||||
REQUIRE_SUCCEEDED(intRef->get_Value(&value));
|
||||
REQUIRE(idx == value);
|
||||
REQUIRE_SUCCEEDED(i.CopyTo(IID_PPV_ARGS(&intRef)));
|
||||
REQUIRE_SUCCEEDED(intRef->get_Value(&value));
|
||||
REQUIRE(idx == value);
|
||||
|
||||
++idx;
|
||||
|
||||
HString rtc;
|
||||
REQUIRE_SUCCEEDED(i->GetRuntimeClassName(rtc.GetAddressOf()));
|
||||
REQUIRE_SUCCEEDED(i->GetRuntimeClassName(rtc.GetAddressOf()));
|
||||
}
|
||||
REQUIRE_SUCCEEDED(success);
|
||||
REQUIRE(count == idx);
|
||||
|
||||
auto strings = MakeSampleStringVector();
|
||||
for (const auto& i : wil::get_range_nothrow(strings.Get(), &success))
|
||||
{
|
||||
REQUIRE(i.Get());
|
||||
REQUIRE(i.Get());
|
||||
}
|
||||
REQUIRE_SUCCEEDED(success);
|
||||
|
||||
int index = 0;
|
||||
auto points = MakeSamplePointVector();
|
||||
for (auto value : wil::get_range_nothrow(points.Get(), &success))
|
||||
{
|
||||
REQUIRE(index++ == value.Get().X);
|
||||
}
|
||||
REQUIRE_SUCCEEDED(success);
|
||||
|
||||
// operator-> should not clear out the pointer
|
||||
auto inspRange = wil::get_range_nothrow(inspectables.Get());
|
||||
for (auto itr = inspRange.begin(); itr != inspRange.end(); ++itr)
|
||||
{
|
||||
REQUIRE(itr->Get());
|
||||
}
|
||||
|
||||
auto strRange = wil::get_range_nothrow(strings.Get());
|
||||
for (auto itr = strRange.begin(); itr != strRange.end(); ++itr)
|
||||
{
|
||||
REQUIRE(itr->Get());
|
||||
}
|
||||
|
||||
index = 0;
|
||||
auto pointRange = wil::get_range_nothrow(points.Get());
|
||||
for (auto itr = pointRange.begin(); itr != pointRange.end(); ++itr)
|
||||
{
|
||||
REQUIRE(index++ == itr->Get().X);
|
||||
}
|
||||
|
||||
#if (defined WIL_ENABLE_EXCEPTIONS)
|
||||
idx = 0;
|
||||
for (const auto& i : wil::get_range(inspectables.Get()))
|
||||
{
|
||||
// Duplications are not a typo - they verify the thing is callable twice
|
||||
|
||||
UINT32 value;
|
||||
ComPtr<IReference<UINT32>> intRef;
|
||||
REQUIRE_SUCCEEDED(i.CopyTo(IID_PPV_ARGS(&intRef)));
|
||||
REQUIRE_SUCCEEDED(intRef->get_Value(&value));
|
||||
REQUIRE(idx == value);
|
||||
REQUIRE_SUCCEEDED(i.CopyTo(IID_PPV_ARGS(&intRef)));
|
||||
REQUIRE_SUCCEEDED(intRef->get_Value(&value));
|
||||
REQUIRE(idx == value);
|
||||
|
||||
++idx;
|
||||
|
||||
HString rtc;
|
||||
REQUIRE_SUCCEEDED(i->GetRuntimeClassName(rtc.GetAddressOf()));
|
||||
REQUIRE_SUCCEEDED(i->GetRuntimeClassName(rtc.GetAddressOf()));
|
||||
}
|
||||
REQUIRE(count == idx);
|
||||
|
||||
for (const auto& i : wil::get_range(strings.Get()))
|
||||
{
|
||||
REQUIRE(i.Get());
|
||||
REQUIRE(i.Get());
|
||||
}
|
||||
|
||||
index = 0;
|
||||
for (auto value : wil::get_range(points.Get()))
|
||||
{
|
||||
REQUIRE(index++ == value.Get().X);
|
||||
}
|
||||
|
||||
// operator-> should not clear out the pointer
|
||||
for (auto itr = inspRange.begin(); itr != inspRange.end(); ++itr)
|
||||
{
|
||||
REQUIRE(itr->Get());
|
||||
}
|
||||
|
||||
for (auto itr = strRange.begin(); itr != strRange.end(); ++itr)
|
||||
{
|
||||
REQUIRE(itr->Get());
|
||||
}
|
||||
|
||||
index = 0;
|
||||
for (auto itr = pointRange.begin(); itr != pointRange.end(); ++itr)
|
||||
{
|
||||
REQUIRE(index++ == itr->Get().X);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned long GetComObjectRefCount(IUnknown* unk) { unk->AddRef(); return unk->Release(); }
|
||||
|
||||
TEST_CASE("WinRTTests::VectorRangeLeakTest", "[winrt][vector_range]")
|
||||
{
|
||||
auto uninit = wil::RoInitialize_failfast();
|
||||
|
||||
auto inspectables = MakeSampleInspectableVector();
|
||||
ComPtr<IInspectable> verifyNotLeaked;
|
||||
HRESULT hr = S_OK;
|
||||
for (const auto& ptr : wil::get_range_nothrow(inspectables.Get(), &hr))
|
||||
{
|
||||
if (!verifyNotLeaked)
|
||||
{
|
||||
verifyNotLeaked = ptr;
|
||||
}
|
||||
}
|
||||
inspectables = nullptr; // clear all refs to verifyNotLeaked
|
||||
REQUIRE_SUCCEEDED(hr);
|
||||
REQUIRE(GetComObjectRefCount(verifyNotLeaked.Get()) == 1);
|
||||
|
||||
inspectables = MakeSampleInspectableVector();
|
||||
for (const auto& ptr : wil::get_range_failfast(inspectables.Get()))
|
||||
{
|
||||
if (!verifyNotLeaked)
|
||||
{
|
||||
verifyNotLeaked = ptr;
|
||||
}
|
||||
}
|
||||
inspectables = nullptr; // clear all refs to verifyNotLeaked
|
||||
REQUIRE(GetComObjectRefCount(verifyNotLeaked.Get()) == 1);
|
||||
|
||||
#if (defined WIL_ENABLE_EXCEPTIONS)
|
||||
inspectables = MakeSampleInspectableVector();
|
||||
for (const auto& ptr : wil::get_range(inspectables.Get()))
|
||||
{
|
||||
if (!verifyNotLeaked)
|
||||
{
|
||||
verifyNotLeaked = ptr;
|
||||
}
|
||||
}
|
||||
inspectables = nullptr; // clear all refs to verifyNotLeaked
|
||||
REQUIRE(GetComObjectRefCount(verifyNotLeaked.Get()) == 1);
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
|
||||
#include <wil/wistd_functional.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "test_objects.h"
|
||||
|
||||
// Test methods/objects
|
||||
int GetValue()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
|
||||
int GetOtherValue()
|
||||
{
|
||||
return 8;
|
||||
}
|
||||
|
||||
int Negate(int value)
|
||||
{
|
||||
return -value;
|
||||
}
|
||||
|
||||
int Add(int lhs, int rhs)
|
||||
{
|
||||
return lhs + rhs;
|
||||
}
|
||||
|
||||
TEST_CASE("WistdFunctionTests::CallOperatorTest", "[wistd]")
|
||||
{
|
||||
wistd::function<int()> getValue = GetValue;
|
||||
REQUIRE(GetValue() == getValue());
|
||||
|
||||
wistd::function<int(int)> negate = Negate;
|
||||
REQUIRE(Negate(42) == negate(42));
|
||||
|
||||
wistd::function<int(int, int)> add = Add;
|
||||
REQUIRE(Add(42, 8) == add(42, 8));
|
||||
}
|
||||
|
||||
TEST_CASE("WistdFunctionTests::AssignmentOperatorTest", "[wistd]")
|
||||
{
|
||||
wistd::function<int()> fn = GetValue;
|
||||
REQUIRE(GetValue() == fn());
|
||||
|
||||
fn = GetOtherValue;
|
||||
REQUIRE(GetOtherValue() == fn());
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
TEST_CASE("WistdFunctionTests::StdFunctionConstructionTest", "[wistd]")
|
||||
{
|
||||
// We should be able to capture a std::function in a wistd::function
|
||||
wistd::function<int()> fn;
|
||||
|
||||
{
|
||||
value_holder holder{ 42 };
|
||||
std::function<int()> stdFn = [holder]()
|
||||
{
|
||||
return holder.value;
|
||||
};
|
||||
|
||||
fn = stdFn;
|
||||
}
|
||||
|
||||
REQUIRE(42 == fn());
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("WistdFunctionTests::CopyConstructionTest", "[wistd]")
|
||||
{
|
||||
object_counter_state state;
|
||||
{
|
||||
wistd::function<int()> copyFrom = [counter = object_counter{ state }]()
|
||||
{
|
||||
return counter.state->copy_count;
|
||||
};
|
||||
REQUIRE(0 == copyFrom());
|
||||
|
||||
auto copyTo = copyFrom;
|
||||
REQUIRE(1 == copyTo());
|
||||
}
|
||||
|
||||
REQUIRE(0 == state.instance_count());
|
||||
}
|
||||
|
||||
TEST_CASE("WistdFunctionTests::CopyAssignmentTest", "[wistd]")
|
||||
{
|
||||
object_counter_state state;
|
||||
{
|
||||
wistd::function<int()> copyTo;
|
||||
{
|
||||
wistd::function<int()> copyFrom = [counter = object_counter{ state }]()
|
||||
{
|
||||
return counter.state->copy_count;
|
||||
};
|
||||
REQUIRE(0 == copyFrom());
|
||||
|
||||
copyTo = copyFrom;
|
||||
}
|
||||
|
||||
REQUIRE(1 == copyTo());
|
||||
}
|
||||
|
||||
REQUIRE(0 == state.instance_count());
|
||||
}
|
||||
|
||||
TEST_CASE("WistdFunctionTests::MoveConstructionTest", "[wistd]")
|
||||
{
|
||||
object_counter_state state;
|
||||
{
|
||||
wistd::function<int()> moveFrom = [counter = object_counter{ state }]()
|
||||
{
|
||||
return counter.state->copy_count;
|
||||
};
|
||||
REQUIRE(0 == moveFrom());
|
||||
|
||||
auto moveTo = std::move(moveFrom);
|
||||
REQUIRE(0 == moveTo());
|
||||
|
||||
// Because we move the underlying function object, we _must_ invalidate the moved from function
|
||||
REQUIRE_FALSE(moveFrom != nullptr);
|
||||
}
|
||||
|
||||
REQUIRE(0 == state.instance_count());
|
||||
}
|
||||
|
||||
TEST_CASE("WistdFunctionTests::MoveAssignmentTest", "[wistd]")
|
||||
{
|
||||
object_counter_state state;
|
||||
{
|
||||
wistd::function<int()> moveTo;
|
||||
{
|
||||
wistd::function<int()> moveFrom = [counter = object_counter{ state }]()
|
||||
{
|
||||
return counter.state->copy_count;
|
||||
};
|
||||
REQUIRE(0 == moveFrom());
|
||||
|
||||
moveTo = std::move(moveFrom);
|
||||
}
|
||||
|
||||
REQUIRE(0 == moveTo());
|
||||
}
|
||||
|
||||
REQUIRE(0 == state.instance_count());
|
||||
}
|
||||
|
||||
TEST_CASE("WistdFunctionTests::SwapTest", "[wistd]")
|
||||
{
|
||||
object_counter_state state;
|
||||
{
|
||||
wistd::function<int()> first;
|
||||
wistd::function<int()> second;
|
||||
|
||||
first.swap(second);
|
||||
REQUIRE_FALSE(first != nullptr);
|
||||
REQUIRE_FALSE(second != nullptr);
|
||||
|
||||
first = [counter = object_counter{ state }]()
|
||||
{
|
||||
return counter.state->copy_count;
|
||||
};
|
||||
|
||||
first.swap(second);
|
||||
REQUIRE_FALSE(first != nullptr);
|
||||
REQUIRE(second != nullptr);
|
||||
REQUIRE(0 == second());
|
||||
|
||||
first.swap(second);
|
||||
REQUIRE(first != nullptr);
|
||||
REQUIRE_FALSE(second != nullptr);
|
||||
REQUIRE(0 == first());
|
||||
|
||||
second = [counter = object_counter{ state }]()
|
||||
{
|
||||
return counter.state->copy_count;
|
||||
};
|
||||
|
||||
first.swap(second);
|
||||
REQUIRE(first != nullptr);
|
||||
REQUIRE(second != nullptr);
|
||||
REQUIRE(0 == first());
|
||||
}
|
||||
|
||||
REQUIRE(0 == state.instance_count());
|
||||
}
|
||||
|
||||
// MSVC's optimizer has had issues with wistd::function in the past when forwarding wistd::function objects to a
|
||||
// function that accepts the arguments by value. This test exercises the workaround that we have in place. Note
|
||||
// that this of course requires building with optimizations enabled
|
||||
void ForwardingTest(wistd::function<int()> getValue, wistd::function<int(int)> negate, wistd::function<int(int, int)> add)
|
||||
{
|
||||
// Previously, this would cause a runtime crash
|
||||
REQUIRE(Add(GetValue(), Negate(8)) == add(getValue(), negate(8)));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void CallForwardingTest(Args&&... args)
|
||||
{
|
||||
ForwardingTest(wistd::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
TEST_CASE("WistdFunctionTests::OptimizationRegressionTest", "[wistd]")
|
||||
{
|
||||
CallForwardingTest(
|
||||
wistd::function<int()>(GetValue),
|
||||
wistd::function<int(int)>(Negate),
|
||||
wistd::function<int(int, int)>(Add));
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
project(witest.app)
|
||||
add_executable(witest.app)
|
||||
|
||||
add_definitions(-DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP)
|
||||
|
||||
target_sources(witest.app PUBLIC
|
||||
${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}/../ResourceTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
|
||||
)
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,507 @@
|
|||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <PathCch.h>
|
||||
|
||||
#include "catch.hpp"
|
||||
|
||||
#include <wil/filesystem.h>
|
||||
#include <wil/result.h>
|
||||
|
||||
#define REPORTS_ERROR(expr) witest::ReportsError(wistd::is_same<HRESULT, decltype(expr)>{}, [&]() { return expr; })
|
||||
#define REQUIRE_ERROR(expr) REQUIRE(REPORTS_ERROR(expr))
|
||||
#define REQUIRE_NOERROR(expr) REQUIRE_FALSE(REPORTS_ERROR(expr))
|
||||
|
||||
#define CRASHES(expr) witest::DoesCodeCrash([&]() { return expr; })
|
||||
#define REQUIRE_CRASH(expr) REQUIRE(CRASHES(expr))
|
||||
#define REQUIRE_NOCRASH(expr) REQUIRE_FALSE(CRASHES(expr))
|
||||
|
||||
// NOTE: SUCCEEDED/FAILED macros not used here since Catch2 can give us better diagnostics if it knows the HRESULT value
|
||||
#define REQUIRE_SUCCEEDED(expr) REQUIRE((HRESULT)(expr) >= 0)
|
||||
#define REQUIRE_FAILED(expr) REQUIRE((HRESULT)(expr) < 0)
|
||||
|
||||
// MACRO double evaluation check.
|
||||
// The following macro illustrates a common problem with writing macros:
|
||||
// #define MY_MAX(a, b) (((a) > (b)) ? (a) : (b))
|
||||
// The issue is that whatever code is being used for both a and b is being executed twice.
|
||||
// This isn't harmful when thinking of constant numerics, but consider this example:
|
||||
// MY_MAX(4, InterlockedIncrement(&cCount))
|
||||
// This evaluates the (B) parameter twice and results in incrementing the counter twice.
|
||||
// We use MDEC in unit tests to verify that this kind of pattern is not present. A test
|
||||
// of this kind:
|
||||
// MY_MAX(MDEC(4), MDEC(InterlockedIncrement(&cCount))
|
||||
// will verify that the parameters are not evaluated more than once.
|
||||
#define MDEC(PARAM) (witest::details::MacroDoubleEvaluationCheck(__LINE__, #PARAM), PARAM)
|
||||
|
||||
// There's some functionality that we need for testing that's not available for the app partition. Since those tests are
|
||||
// primarily compilation tests, declare what's needed here
|
||||
extern "C" {
|
||||
|
||||
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM | WINAPI_PARTITION_GAMES)
|
||||
WINBASEAPI _Ret_maybenull_
|
||||
PVOID WINAPI AddVectoredExceptionHandler(_In_ ULONG First, _In_ PVECTORED_EXCEPTION_HANDLER Handler);
|
||||
|
||||
WINBASEAPI
|
||||
ULONG WINAPI RemoveVectoredExceptionHandler(_In_ PVOID Handle);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4702) // Unreachable code
|
||||
|
||||
namespace witest
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
inline void MacroDoubleEvaluationCheck(size_t uLine, _In_ const char* pszCode)
|
||||
{
|
||||
struct SEval
|
||||
{
|
||||
size_t uLine;
|
||||
const char* pszCode;
|
||||
};
|
||||
|
||||
static SEval rgEval[15] = {};
|
||||
static size_t nOffset = 0;
|
||||
|
||||
for (auto& eval : rgEval)
|
||||
{
|
||||
if ((eval.uLine == uLine) && (eval.pszCode != nullptr) && (0 == strcmp(pszCode, eval.pszCode)))
|
||||
{
|
||||
// This verification indicates that macro-double-evaluation check is firing for a particular usage of MDEC().
|
||||
FAIL("Expression '" << pszCode << "' double evaluated in macro on line " << uLine);
|
||||
}
|
||||
}
|
||||
|
||||
rgEval[nOffset].uLine = uLine;
|
||||
rgEval[nOffset].pszCode = pszCode;
|
||||
nOffset = (nOffset + 1) % ARRAYSIZE(rgEval);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class AssignTemporaryValueCleanup
|
||||
{
|
||||
public:
|
||||
AssignTemporaryValueCleanup(_In_ AssignTemporaryValueCleanup const &) = delete;
|
||||
AssignTemporaryValueCleanup & operator=(_In_ AssignTemporaryValueCleanup const &) = delete;
|
||||
|
||||
explicit AssignTemporaryValueCleanup(_Inout_ T *pVal, T val) WI_NOEXCEPT :
|
||||
m_pVal(pVal),
|
||||
m_valOld(*pVal)
|
||||
{
|
||||
*pVal = val;
|
||||
}
|
||||
|
||||
AssignTemporaryValueCleanup(_Inout_ AssignTemporaryValueCleanup && other) WI_NOEXCEPT :
|
||||
m_pVal(other.m_pVal),
|
||||
m_valOld(other.m_valOld)
|
||||
{
|
||||
other.m_pVal = nullptr;
|
||||
}
|
||||
|
||||
~AssignTemporaryValueCleanup() WI_NOEXCEPT
|
||||
{
|
||||
operator()();
|
||||
}
|
||||
|
||||
void operator()() WI_NOEXCEPT
|
||||
{
|
||||
if (m_pVal != nullptr)
|
||||
{
|
||||
*m_pVal = m_valOld;
|
||||
m_pVal = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Dismiss() WI_NOEXCEPT
|
||||
{
|
||||
m_pVal = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
T *m_pVal;
|
||||
T m_valOld;
|
||||
};
|
||||
}
|
||||
|
||||
// Use the following routine to allow for a variable to be swapped with another and automatically revert the
|
||||
// assignment at the end of the scope.
|
||||
// Example:
|
||||
// int nFoo = 10
|
||||
// {
|
||||
// auto revert = witest::AssignTemporaryValue(&nFoo, 12);
|
||||
// // nFoo will now be 12 within this scope...
|
||||
// }
|
||||
// // and nFoo is back to 10 within the outer scope
|
||||
template <typename T>
|
||||
inline witest::details::AssignTemporaryValueCleanup<T> AssignTemporaryValue(_Inout_ T *pVal, T val) WI_NOEXCEPT
|
||||
{
|
||||
return witest::details::AssignTemporaryValueCleanup<T>(pVal, val);
|
||||
}
|
||||
|
||||
//! Global class which tracks objects that derive from @ref AllocatedObject.
|
||||
//! Use `witest::g_objectCount.Leaked()` to determine if an object deriving from `AllocatedObject` has been leaked.
|
||||
class GlobalCount
|
||||
{
|
||||
public:
|
||||
int m_count = 0;
|
||||
|
||||
//! Returns `true` if there are any objects that derive from @ref AllocatedObject still in memory.
|
||||
bool Leaked() const
|
||||
{
|
||||
return (m_count != 0);
|
||||
}
|
||||
|
||||
~GlobalCount()
|
||||
{
|
||||
if (Leaked())
|
||||
{
|
||||
// NOTE: This runs when no test is active, but will still cause an assert failure to notify
|
||||
FAIL("GlobalCount is non-zero; there is a leak somewhere");
|
||||
}
|
||||
}
|
||||
};
|
||||
__declspec(selectany) GlobalCount g_objectCount;
|
||||
|
||||
//! Derive an allocated test object from witest::AllocatedObject to ensure that those objects aren't leaked in the test.
|
||||
//! Note that you can call g_objectCount.Leaked() at any point to determine if a leak has already occurred (assuming that
|
||||
//! all objects should have been destroyed at that point.
|
||||
class AllocatedObject
|
||||
{
|
||||
public:
|
||||
AllocatedObject() { g_objectCount.m_count++; }
|
||||
~AllocatedObject() { g_objectCount.m_count--; }
|
||||
};
|
||||
|
||||
template <typename Lambda>
|
||||
bool DoesCodeThrow(Lambda&& callOp)
|
||||
{
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
try
|
||||
#endif
|
||||
{
|
||||
callOp();
|
||||
}
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
catch (...)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
inline void __stdcall TranslateFailFastException(PEXCEPTION_RECORD rec, PCONTEXT, DWORD)
|
||||
{
|
||||
// RaiseFailFastException cannot be continued or handled. By instead calling RaiseException, it allows us to
|
||||
// handle exceptions
|
||||
::RaiseException(rec->ExceptionCode, rec->ExceptionFlags, rec->NumberParameters, rec->ExceptionInformation);
|
||||
}
|
||||
|
||||
constexpr DWORD msvc_exception_code = 0xE06D7363;
|
||||
|
||||
// This is a MAJOR hack. Catch2 registers a vectored exception handler - which gets run before our handler below -
|
||||
// that interprets a set of exception codes as fatal. We don't want this behavior since we may be expecting such
|
||||
// crashes, so instead translate all exception codes to something not fatal
|
||||
inline LONG WINAPI TranslateExceptionCodeHandler(PEXCEPTION_POINTERS info)
|
||||
{
|
||||
if (info->ExceptionRecord->ExceptionCode != witest::msvc_exception_code)
|
||||
{
|
||||
info->ExceptionRecord->ExceptionCode = STATUS_STACK_BUFFER_OVERRUN;
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
namespace details
|
||||
{
|
||||
inline bool DoesCodeCrash(wistd::function<void()>& callOp)
|
||||
{
|
||||
bool result = false;
|
||||
__try
|
||||
{
|
||||
callOp();
|
||||
}
|
||||
// Let C++ exceptions pass through
|
||||
__except ((::GetExceptionCode() != msvc_exception_code) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool DoesCodeCrash(wistd::function<void()> callOp)
|
||||
{
|
||||
// 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 handler = AddVectoredExceptionHandler(1, TranslateExceptionCodeHandler);
|
||||
auto removeVectoredHandler = wil::scope_exit([&] { RemoveVectoredExceptionHandler(handler); });
|
||||
|
||||
return details::DoesCodeCrash(callOp);
|
||||
}
|
||||
|
||||
template <typename Lambda>
|
||||
bool ReportsError(wistd::false_type, Lambda&& callOp)
|
||||
{
|
||||
bool doesThrow = false;
|
||||
bool doesCrash = DoesCodeCrash([&]()
|
||||
{
|
||||
doesThrow = DoesCodeThrow(callOp);
|
||||
});
|
||||
|
||||
return doesThrow || doesCrash;
|
||||
}
|
||||
|
||||
template <typename Lambda>
|
||||
bool ReportsError(wistd::true_type, Lambda&& callOp)
|
||||
{
|
||||
return FAILED(callOp());
|
||||
}
|
||||
|
||||
#ifdef WIL_ENABLE_EXCEPTIONS
|
||||
class TestFailureCache final :
|
||||
public wil::details::IFailureCallback
|
||||
{
|
||||
public:
|
||||
TestFailureCache() :
|
||||
m_callbackHolder(this)
|
||||
{
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
m_failures.clear();
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return m_failures.size();
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return m_failures.empty();
|
||||
}
|
||||
|
||||
const wil::FailureInfo& operator[](size_t pos) const
|
||||
{
|
||||
return m_failures.at(pos).GetFailureInfo();
|
||||
}
|
||||
|
||||
// IFailureCallback
|
||||
bool NotifyFailure(wil::FailureInfo const & failure) WI_NOEXCEPT override
|
||||
{
|
||||
m_failures.emplace_back(failure);
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<wil::StoredFailureInfo> m_failures;
|
||||
wil::details::ThreadFailureCallbackHolder m_callbackHolder;
|
||||
};
|
||||
#endif
|
||||
|
||||
inline HRESULT GetTempFileName(wchar_t (&result)[MAX_PATH])
|
||||
{
|
||||
wchar_t dir[MAX_PATH];
|
||||
RETURN_LAST_ERROR_IF(::GetTempPathW(MAX_PATH, dir) == 0);
|
||||
RETURN_LAST_ERROR_IF(::GetTempFileNameW(dir, L"wil", 0, result) == 0);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
inline HRESULT CreateUniqueFolderPath(wchar_t (&buffer)[MAX_PATH], PCWSTR root = nullptr)
|
||||
{
|
||||
if (root)
|
||||
{
|
||||
RETURN_LAST_ERROR_IF(::GetTempFileNameW(root, L"wil", 0, buffer) == 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
wchar_t tempPath[MAX_PATH];
|
||||
RETURN_LAST_ERROR_IF(::GetTempPathW(ARRAYSIZE(tempPath), tempPath) == 0);
|
||||
RETURN_LAST_ERROR_IF(::GetLongPathNameW(tempPath, tempPath, ARRAYSIZE(tempPath)) == 0);
|
||||
RETURN_LAST_ERROR_IF(::GetTempFileNameW(tempPath, L"wil", 0, buffer) == 0);
|
||||
}
|
||||
RETURN_IF_WIN32_BOOL_FALSE(DeleteFileW(buffer));
|
||||
PathCchRemoveExtension(buffer, ARRAYSIZE(buffer));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
|
||||
struct TestFolder
|
||||
{
|
||||
TestFolder()
|
||||
{
|
||||
if (SUCCEEDED(CreateUniqueFolderPath(m_path)) && SUCCEEDED(wil::CreateDirectoryDeepNoThrow(m_path)))
|
||||
{
|
||||
m_valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
TestFolder(PCWSTR path)
|
||||
{
|
||||
if (SUCCEEDED(StringCchCopyW(m_path, ARRAYSIZE(m_path), path)) && SUCCEEDED(wil::CreateDirectoryDeepNoThrow(m_path)))
|
||||
{
|
||||
m_valid = true;
|
||||
}
|
||||
}
|
||||
|
||||
TestFolder(const TestFolder&) = delete;
|
||||
TestFolder& operator=(const TestFolder&) = delete;
|
||||
|
||||
TestFolder(TestFolder&& other)
|
||||
{
|
||||
if (other.m_valid)
|
||||
{
|
||||
m_valid = true;
|
||||
other.m_valid = false;
|
||||
wcscpy_s(m_path, other.m_path);
|
||||
}
|
||||
}
|
||||
|
||||
~TestFolder()
|
||||
{
|
||||
if (m_valid)
|
||||
{
|
||||
wil::RemoveDirectoryRecursiveNoThrow(m_path);
|
||||
}
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
operator PCWSTR() const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
PCWSTR Path() const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
bool m_valid = false;
|
||||
wchar_t m_path[MAX_PATH] = L"";
|
||||
};
|
||||
|
||||
struct TestFile
|
||||
{
|
||||
TestFile(PCWSTR path)
|
||||
{
|
||||
if (SUCCEEDED(StringCchCopyW(m_path, ARRAYSIZE(m_path), path)))
|
||||
{
|
||||
Create();
|
||||
}
|
||||
}
|
||||
|
||||
TestFile(PCWSTR dirPath, PCWSTR fileName)
|
||||
{
|
||||
if (SUCCEEDED(StringCchCopyW(m_path, ARRAYSIZE(m_path), dirPath)) && SUCCEEDED(PathCchAppend(m_path, ARRAYSIZE(m_path), fileName)))
|
||||
{
|
||||
Create();
|
||||
}
|
||||
}
|
||||
|
||||
TestFile(const TestFile&) = delete;
|
||||
TestFile& operator=(const TestFile&) = delete;
|
||||
|
||||
TestFile(TestFile&& other)
|
||||
{
|
||||
if (other.m_valid)
|
||||
{
|
||||
m_valid = true;
|
||||
m_deleteDir = other.m_deleteDir;
|
||||
other.m_valid = other.m_deleteDir = false;
|
||||
wcscpy_s(m_path, other.m_path);
|
||||
}
|
||||
}
|
||||
|
||||
~TestFile()
|
||||
{
|
||||
// Best effort on all of these
|
||||
if (m_valid)
|
||||
{
|
||||
::DeleteFileW(m_path);
|
||||
}
|
||||
if (m_deleteDir)
|
||||
{
|
||||
size_t parentLength;
|
||||
if (wil::try_get_parent_path_range(m_path, &parentLength))
|
||||
{
|
||||
m_path[parentLength] = L'\0';
|
||||
::RemoveDirectoryW(m_path);
|
||||
m_path[parentLength] = L'\\';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
operator PCWSTR() const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
PCWSTR Path() const
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
HRESULT Create()
|
||||
{
|
||||
WI_ASSERT(!m_valid && !m_deleteDir);
|
||||
wil::unique_hfile fileHandle(::CreateFileW(m_path,
|
||||
FILE_WRITE_ATTRIBUTES,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
|
||||
if (!fileHandle)
|
||||
{
|
||||
auto err = ::GetLastError();
|
||||
size_t parentLength;
|
||||
if ((err == ERROR_PATH_NOT_FOUND) && wil::try_get_parent_path_range(m_path, &parentLength))
|
||||
{
|
||||
m_path[parentLength] = L'\0';
|
||||
RETURN_IF_FAILED(wil::CreateDirectoryDeepNoThrow(m_path));
|
||||
m_deleteDir = true;
|
||||
|
||||
m_path[parentLength] = L'\\';
|
||||
fileHandle.reset(::CreateFileW(m_path,
|
||||
FILE_WRITE_ATTRIBUTES,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
|
||||
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
|
||||
RETURN_LAST_ERROR_IF(!fileHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
RETURN_WIN32(err);
|
||||
}
|
||||
}
|
||||
|
||||
m_valid = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
bool m_valid = false;
|
||||
bool m_deleteDir = false;
|
||||
wchar_t m_path[MAX_PATH] = L"";
|
||||
};
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma warning(pop)
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
# 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
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
project(witest.cpplatest)
|
||||
add_executable(witest.cpplatest)
|
||||
|
||||
target_sources(witest.cpplatest PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRTTests.cpp
|
||||
${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}/../SafeCastTests.cpp
|
||||
${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}/../WistdTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
|
||||
)
|
|
@ -0,0 +1,7 @@
|
|||
|
||||
#pragma comment(lib, "Pathcch.lib")
|
||||
#pragma comment(lib, "RuntimeObject.lib")
|
||||
#pragma comment(lib, "Synchronization.lib")
|
||||
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include "catch.hpp"
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
project(witest.noexcept)
|
||||
add_executable(witest.noexcept)
|
||||
|
||||
# Turn off exceptions for this test
|
||||
replace_cxx_flag("/EHsc" "")
|
||||
add_definitions(-DCATCH_CONFIG_DISABLE_EXCEPTIONS)
|
||||
|
||||
# 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
|
||||
append_cxx_flag("/wd4530")
|
||||
|
||||
target_sources(witest.noexcept PUBLIC
|
||||
${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}/../ResourceTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.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}/../WistdTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp)
|
|
@ -0,0 +1,20 @@
|
|||
|
||||
project(witest)
|
||||
add_executable(witest)
|
||||
|
||||
target_sources(witest PUBLIC
|
||||
${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}/../ResourceTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp
|
||||
${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}/../WistdTests.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp
|
||||
)
|
|
@ -0,0 +1,107 @@
|
|||
#pragma once
|
||||
|
||||
#include "catch.hpp"
|
||||
|
||||
// Useful for validating that the copy constructor is never called (e.g. to validate perfect forwarding). Note that
|
||||
// the copy constructor/assignment operator are not deleted since we want to be able to validate in scenarios that
|
||||
// require CopyConstructible (e.g. for wistd::function)
|
||||
struct fail_on_copy
|
||||
{
|
||||
fail_on_copy() = default;
|
||||
|
||||
fail_on_copy(const fail_on_copy&)
|
||||
{
|
||||
FAIL("Copy constructor invoked for fail_on_copy type");
|
||||
}
|
||||
|
||||
fail_on_copy(fail_on_copy&&) = default;
|
||||
|
||||
fail_on_copy& operator=(const fail_on_copy&)
|
||||
{
|
||||
FAIL("Copy assignment operator invoked for fail_on_copy type");
|
||||
return *this;
|
||||
}
|
||||
|
||||
fail_on_copy& operator=(fail_on_copy&&) = default;
|
||||
};
|
||||
|
||||
// Useful for validating that objects get copied e.g. as opposed to capturing a reference
|
||||
struct value_holder
|
||||
{
|
||||
int value = 0xbadf00d;
|
||||
|
||||
~value_holder()
|
||||
{
|
||||
value = 0xbadf00d;
|
||||
}
|
||||
};
|
||||
|
||||
// Useful for validating that functions, etc. are callable with move-only types
|
||||
// Example real type that is move only is Microsoft::WRL::Wrappers::HString
|
||||
struct cannot_copy
|
||||
{
|
||||
cannot_copy(const cannot_copy&) = delete;
|
||||
cannot_copy& operator=(const cannot_copy&) = delete;
|
||||
|
||||
cannot_copy(cannot_copy&&) = default;
|
||||
cannot_copy& operator=(cannot_copy&&) = default;
|
||||
};
|
||||
|
||||
// State for object_counter type. This has the unfortunate side effect that the object_counter type cannot be used in
|
||||
// contexts that require a default constructible type, but has the nice property that it allows for tests to run
|
||||
// concurrently
|
||||
struct object_counter_state
|
||||
{
|
||||
volatile LONG constructed_count = 0;
|
||||
volatile LONG destructed_count = 0;
|
||||
volatile LONG copy_count = 0;
|
||||
volatile LONG move_count = 0;
|
||||
|
||||
LONG instance_count()
|
||||
{
|
||||
return constructed_count - destructed_count;
|
||||
}
|
||||
};
|
||||
|
||||
struct object_counter
|
||||
{
|
||||
object_counter_state* state;
|
||||
|
||||
object_counter(object_counter_state& s) :
|
||||
state(&s)
|
||||
{
|
||||
::InterlockedIncrement(&state->constructed_count);
|
||||
}
|
||||
|
||||
object_counter(const object_counter& other) :
|
||||
state(other.state)
|
||||
{
|
||||
::InterlockedIncrement(&state->constructed_count);
|
||||
::InterlockedIncrement(&state->copy_count);
|
||||
}
|
||||
|
||||
object_counter(object_counter&& other) :
|
||||
state(other.state)
|
||||
{
|
||||
::InterlockedIncrement(&state->constructed_count);
|
||||
::InterlockedIncrement(&state->move_count);
|
||||
}
|
||||
|
||||
~object_counter()
|
||||
{
|
||||
::InterlockedIncrement(&state->destructed_count);
|
||||
state = nullptr;
|
||||
}
|
||||
|
||||
object_counter& operator=(const object_counter&)
|
||||
{
|
||||
::InterlockedIncrement(&state->copy_count);
|
||||
return *this;
|
||||
}
|
||||
|
||||
object_counter& operator=(object_counter&&)
|
||||
{
|
||||
::InterlockedIncrement(&state->move_count);
|
||||
return *this;
|
||||
}
|
||||
};
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,2 @@
|
|||
|
||||
We try and be as conformant as possible, but sometimes dependencies make that difficult. For example, WRL has had a number of conformance issues that keep getting uncovered. The files here are fixed up copies of those files and the include path is modified such that these directories appear first.
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче