From 5f4caba4e7a9017816e47becdd918fcc872039ba Mon Sep 17 00:00:00 2001 From: Duncan Horn <40036384+dunhor@users.noreply.github.com> Date: Wed, 14 Sep 2022 16:27:58 -0700 Subject: [PATCH] Sync with the OS & fix some issues (#258) * Sync with OS commit 7304518c5f69f8c5d9d05ed36cc79c7cb0396b5e * Build fixes * Unreachable code warnings * Fixes to get kernel code building again * Revert "Revert result macros to be noexcept again (#246)" This reverts commit d84d4e629f1490f91e8ed983f324084f4571a75f. --- include/wil/com.h | 10 +-- include/wil/filesystem.h | 25 +++++- include/wil/resource.h | 72 +++++++++++++++ include/wil/result_macros.h | 173 ++++++++++++++---------------------- include/wil/win32_helpers.h | 131 +++++++++++++++++++++++++++ 5 files changed, 295 insertions(+), 116 deletions(-) diff --git a/include/wil/com.h b/include/wil/com.h index 280d8e836..76d54f554 100644 --- a/include/wil/com.h +++ b/include/wil/com.h @@ -1294,7 +1294,7 @@ namespace wil auto raw = com_raw_ptr(wistd::forward(ptrSource)); auto hr = details::query_policy_t::query(raw, ptrResult); __analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr)); - RETURN_HR(hr); + return hr; } #ifdef WIL_ENABLE_EXCEPTIONS @@ -1336,7 +1336,7 @@ namespace wil auto raw = com_raw_ptr(wistd::forward(ptrSource)); auto hr = details::query_policy_t::query(raw, riid, ptrResult); __analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr)); - RETURN_HR(hr); + return hr; } //! @} @@ -1726,7 +1726,7 @@ namespace wil auto raw = com_raw_ptr(wistd::forward(ptrSource)); auto hr = ::RoGetAgileReference(options, __uuidof(raw), raw, ptrResult); __analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr)); - RETURN_HR(hr); + return hr; } #ifdef WIL_ENABLE_EXCEPTIONS @@ -1841,7 +1841,7 @@ namespace wil auto raw = com_raw_ptr(wistd::forward(ptrSource)); auto hr = details::GetWeakReference(raw, ptrResult); __analysis_assume(SUCCEEDED(hr) || (*ptrResult == nullptr)); - RETURN_HR(hr); + return hr; } #ifdef WIL_ENABLE_EXCEPTIONS @@ -2076,7 +2076,7 @@ namespace wil { return std::tuple{E_NOINTERFACE, {}}; } - return std::tuple{S_OK, result}; + return std::tuple{error, result}; } template diff --git a/include/wil/filesystem.h b/include/wil/filesystem.h index f2c723a1a..def11b625 100644 --- a/include/wil/filesystem.h +++ b/include/wil/filesystem.h @@ -339,9 +339,28 @@ namespace wil { if (deleteHandle != INVALID_HANDLE_VALUE) { - FILE_DISPOSITION_INFO fileInfo{}; - fileInfo.DeleteFile = TRUE; - RETURN_IF_WIN32_BOOL_FALSE(SetFileInformationByHandle(deleteHandle, FileDispositionInfo, &fileInfo, sizeof(fileInfo))); +#if (NTDDI_VERSION >= NTDDI_WIN10_RS1) + // DeleteFile and RemoveDirectory use POSIX delete, falling back to non-POSIX on most errors. Do the same here. + FILE_DISPOSITION_INFO_EX fileInfoEx{}; + fileInfoEx.Flags = FILE_DISPOSITION_FLAG_DELETE | FILE_DISPOSITION_FLAG_POSIX_SEMANTICS; + if (!SetFileInformationByHandle(deleteHandle, FileDispositionInfoEx, &fileInfoEx, sizeof(fileInfoEx))) + { + auto const err = ::GetLastError(); + // The real error we're looking for is STATUS_CANNOT_DELETE, but that's mapped to ERROR_ACCESS_DENIED. + if (err != ERROR_ACCESS_DENIED) + { +#endif + FILE_DISPOSITION_INFO fileInfo{}; + fileInfo.DeleteFile = TRUE; + RETURN_IF_WIN32_BOOL_FALSE(SetFileInformationByHandle(deleteHandle, FileDispositionInfo, &fileInfo, sizeof(fileInfo))); +#if (NTDDI_VERSION >= NTDDI_WIN10_RS1) + } + else + { + RETURN_WIN32(err); + } + } +#endif } else { diff --git a/include/wil/resource.h b/include/wil/resource.h index ce9ead66f..a9496cb7f 100644 --- a/include/wil/resource.h +++ b/include/wil/resource.h @@ -42,6 +42,78 @@ namespace Microsoft namespace wil { + //! This type copies the current value of GetLastError at construction and resets the last error + //! to that value when it is destroyed. + //! + //! This is useful in library code that runs during a value's destructor. If the library code could + //! inadvertantly change the value of GetLastError (by calling a Win32 API or similar), it should + //! instantiate a value of this type before calling the library function in order to preserve the + //! GetLastError value the user would expect. + //! + //! This construct exists to hide kernel mode/user mode differences in wil library code. + //! + //! Example usage: + //! + //! if (!CreateFile(...)) + //! { + //! auto lastError = wil::last_error_context(); + //! WriteFile(g_hlog, logdata); + //! } + //! + class last_error_context + { +#ifndef WIL_KERNEL_MODE + bool m_dismissed = false; + DWORD m_error = 0; + public: + last_error_context() WI_NOEXCEPT : last_error_context(::GetLastError()) + { + } + + explicit last_error_context(DWORD error) WI_NOEXCEPT : + m_error(error) + { + } + + last_error_context(last_error_context&& other) WI_NOEXCEPT + { + operator=(wistd::move(other)); + } + + last_error_context& operator=(last_error_context&& other) WI_NOEXCEPT + { + m_dismissed = wistd::exchange(other.m_dismissed, true); + m_error = other.m_error; + + return *this; + } + + ~last_error_context() WI_NOEXCEPT + { + if (!m_dismissed) + { + ::SetLastError(m_error); + } + } + + //! last_error_context doesn't own a concrete resource, so therefore + //! it just disarms its destructor and returns void. + void release() WI_NOEXCEPT + { + WI_ASSERT(!m_dismissed); + m_dismissed = true; + } + + auto value() const WI_NOEXCEPT + { + return m_error; + } +#else + public: + void release() WI_NOEXCEPT { } +#endif // WIL_KERNEL_MODE + }; + /// @cond namespace details { diff --git a/include/wil/result_macros.h b/include/wil/result_macros.h index 633dbbbe8..aa64cc6dc 100644 --- a/include/wil/result_macros.h +++ b/include/wil/result_macros.h @@ -40,7 +40,7 @@ // constructible. Therefore, use 'sizeof' for syntax validation. We don't do this universally for all compilers // since lambdas are not allowed in unevaluated contexts prior to C++20, which does not appear to affect __noop #if !defined(_MSC_VER) || defined(__clang__) -#define __WI_ANALYSIS_ASSUME(_exp) ((void)sizeof(_exp)) // Validate syntax on non-debug builds +#define __WI_ANALYSIS_ASSUME(_exp) ((void)sizeof(!(_exp))) // Validate syntax on non-debug builds #else #define __WI_ANALYSIS_ASSUME(_exp) __noop(_exp) #endif @@ -2258,78 +2258,6 @@ __WI_POP_WARNINGS } // details namespace /// @endcond - //! This type copies the current value of GetLastError at construction and resets the last error - //! to that value when it is destroyed. - //! - //! This is useful in library code that runs during a value's destructor. If the library code could - //! inadvertantly change the value of GetLastError (by calling a Win32 API or similar), it should - //! instantiate a value of this type before calling the library function in order to preserve the - //! GetLastError value the user would expect. - //! - //! This construct exists to hide kernel mode/user mode differences in wil library code. - //! - //! Example usage: - //! - //! if (!CreateFile(...)) - //! { - //! auto lastError = wil::last_error_context(); - //! WriteFile(g_hlog, logdata); - //! } - //! - class last_error_context - { -#ifndef WIL_KERNEL_MODE - bool m_dismissed = false; - DWORD m_error = 0; - public: - last_error_context() WI_NOEXCEPT : last_error_context(::GetLastError()) - { - } - - explicit last_error_context(DWORD error) WI_NOEXCEPT : - m_error(error) - { - } - - last_error_context(last_error_context&& other) WI_NOEXCEPT - { - operator=(wistd::move(other)); - } - - last_error_context& operator=(last_error_context&& other) WI_NOEXCEPT - { - m_dismissed = wistd::exchange(other.m_dismissed, true); - m_error = other.m_error; - - return *this; - } - - ~last_error_context() WI_NOEXCEPT - { - if (!m_dismissed) - { - ::SetLastError(m_error); - } - } - - //! last_error_context doesn't own a concrete resource, so therefore - //! it just disarms its destructor and returns void. - void release() WI_NOEXCEPT - { - WI_ASSERT(!m_dismissed); - m_dismissed = true; - } - - auto value() const WI_NOEXCEPT - { - return m_error; - } -#else - public: - void release() WI_NOEXCEPT { } -#endif // WIL_KERNEL_MODE - }; - //***************************************************************************** // WIL result handling initializers // @@ -4016,24 +3944,28 @@ __WI_SUPPRESS_4127_E template __declspec(noinline) inline DWORD ReportFailure_GetLastError(__R_FN_PARAMS_FULL) { - const last_error_context err { GetLastErrorFail(__R_FN_CALL_FULL) }; - ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(__HRESULT_FROM_WIN32(err.value()))); - return err.value(); + const auto err = GetLastErrorFail(__R_FN_CALL_FULL); + const auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); + ::SetLastError(err); + return err; } template<> __declspec(noinline) inline RESULT_NORETURN DWORD ReportFailure_GetLastError(__R_FN_PARAMS_FULL) { - const last_error_context err { GetLastErrorFail(__R_FN_CALL_FULL) }; - ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(__HRESULT_FROM_WIN32(err.value()))); + const auto err = GetLastErrorFail(__R_FN_CALL_FULL); + const auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); RESULT_NORETURN_RESULT(err); } template<> __declspec(noinline) inline RESULT_NORETURN DWORD ReportFailure_GetLastError(__R_FN_PARAMS_FULL) { - const last_error_context err { GetLastErrorFail(__R_FN_CALL_FULL) }; - ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(__HRESULT_FROM_WIN32(err.value()))); + const auto err = GetLastErrorFail(__R_FN_CALL_FULL); + const auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Base(__R_FN_CALL_FULL, ResultStatus::FromResult(hr)); RESULT_NORETURN_RESULT(err); } @@ -4172,24 +4104,28 @@ __WI_SUPPRESS_4127_E template __declspec(noinline) inline DWORD ReportFailure_GetLastErrorMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) { - const last_error_context err { GetLastErrorFail(__R_FN_CALL_FULL) }; - ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(__HRESULT_FROM_WIN32(err.value())), formatString, argList); - return err.value(); + auto err = GetLastErrorFail(__R_FN_CALL_FULL); + auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); + ::SetLastError(err); + return err; } template<> __declspec(noinline) inline RESULT_NORETURN DWORD ReportFailure_GetLastErrorMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) { - const last_error_context err { GetLastErrorFail(__R_FN_CALL_FULL) }; - ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(__HRESULT_FROM_WIN32(err.value())), formatString, argList); + auto err = GetLastErrorFail(__R_FN_CALL_FULL); + auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); RESULT_NORETURN_RESULT(err); } template<> __declspec(noinline) inline RESULT_NORETURN DWORD ReportFailure_GetLastErrorMsg(__R_FN_PARAMS_FULL, _Printf_format_string_ PCSTR formatString, va_list argList) { - const last_error_context err { GetLastErrorFail(__R_FN_CALL_FULL) }; - ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(__HRESULT_FROM_WIN32(err.value())), formatString, argList); + auto err = GetLastErrorFail(__R_FN_CALL_FULL); + auto hr = __HRESULT_FROM_WIN32(err); + ReportFailure_Msg(__R_FN_CALL_FULL, ResultStatus::FromResult(hr), formatString, argList); RESULT_NORETURN_RESULT(err); } @@ -4522,8 +4458,9 @@ __WI_SUPPRESS_4127_E wil::details::ReportFailure_NtStatus(__R_INTERNAL_FN_CALL status); } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. _Post_satisfies_(return == hr) - __R_CONDITIONAL_METHOD(HRESULT, Log_IfFailed)(__R_CONDITIONAL_FN_PARAMS HRESULT hr) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(HRESULT, Log_IfFailed)(__R_CONDITIONAL_FN_PARAMS HRESULT hr) { if (FAILED(hr)) { @@ -4559,8 +4496,9 @@ __WI_SUPPRESS_4127_E return hr; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. _Post_satisfies_(return == ret) - __R_CONDITIONAL_METHOD(BOOL, Log_IfWin32BoolFalse)(__R_CONDITIONAL_FN_PARAMS BOOL ret) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(BOOL, Log_IfWin32BoolFalse)(__R_CONDITIONAL_FN_PARAMS BOOL ret) { if (!ret) { @@ -4569,8 +4507,9 @@ __WI_SUPPRESS_4127_E return ret; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. _Post_satisfies_(return == err) - __R_CONDITIONAL_METHOD(DWORD, Log_IfWin32Error)(__R_CONDITIONAL_FN_PARAMS DWORD err) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(DWORD, Log_IfWin32Error)(__R_CONDITIONAL_FN_PARAMS DWORD err) { if (FAILED_WIN32(err)) { @@ -4579,8 +4518,9 @@ __WI_SUPPRESS_4127_E return err; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. _Post_satisfies_(return == handle) - __R_CONDITIONAL_METHOD(HANDLE, Log_IfHandleInvalid)(__R_CONDITIONAL_FN_PARAMS HANDLE handle) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(HANDLE, Log_IfHandleInvalid)(__R_CONDITIONAL_FN_PARAMS HANDLE handle) { if (handle == INVALID_HANDLE_VALUE) { @@ -4589,8 +4529,9 @@ __WI_SUPPRESS_4127_E return handle; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. _Post_satisfies_(return == handle) - __R_CONDITIONAL_METHOD(HANDLE, Log_IfHandleNull)(__R_CONDITIONAL_FN_PARAMS HANDLE handle) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(HANDLE, Log_IfHandleNull)(__R_CONDITIONAL_FN_PARAMS HANDLE handle) { if (handle == nullptr) { @@ -4619,8 +4560,9 @@ __WI_SUPPRESS_4127_E } } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. _Post_satisfies_(return == condition) - __R_CONDITIONAL_METHOD(bool, Log_HrIf)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(bool, Log_HrIf)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) { if (condition) { @@ -4629,8 +4571,9 @@ __WI_SUPPRESS_4127_E return condition; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. _Post_satisfies_(return == condition) - __R_CONDITIONAL_METHOD(bool, Log_HrIfFalse)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(bool, Log_HrIfFalse)(__R_CONDITIONAL_FN_PARAMS HRESULT hr, bool condition) { if (!condition) { @@ -4659,8 +4602,9 @@ __WI_SUPPRESS_4127_E } } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. _Post_satisfies_(return == condition) - __R_CONDITIONAL_METHOD(bool, Log_GetLastErrorIf)(__R_CONDITIONAL_FN_PARAMS bool condition) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(bool, Log_GetLastErrorIf)(__R_CONDITIONAL_FN_PARAMS bool condition) { if (condition) { @@ -4669,8 +4613,9 @@ __WI_SUPPRESS_4127_E return condition; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. _Post_satisfies_(return == condition) - __R_CONDITIONAL_METHOD(bool, Log_GetLastErrorIfFalse)(__R_CONDITIONAL_FN_PARAMS bool condition) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(bool, Log_GetLastErrorIfFalse)(__R_CONDITIONAL_FN_PARAMS bool condition) { if (!condition) { @@ -4699,8 +4644,9 @@ __WI_SUPPRESS_4127_E } } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. _Post_satisfies_(return == status) - __R_CONDITIONAL_METHOD(NTSTATUS, Log_IfNtStatusFailed)(__R_CONDITIONAL_FN_PARAMS NTSTATUS status) WI_NOEXCEPT + __R_CONDITIONAL_METHOD(NTSTATUS, Log_IfNtStatusFailed)(__R_CONDITIONAL_FN_PARAMS NTSTATUS status) { if (FAILED_NTSTATUS(status)) { @@ -5068,8 +5014,9 @@ __WI_SUPPRESS_4127_E return ret; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. _Post_satisfies_(return == err) _When_(FAILED_WIN32(err), _Analysis_noreturn_) - __RFF_CONDITIONAL_METHOD(DWORD, FailFast_IfWin32Error)(__RFF_CONDITIONAL_FN_PARAMS DWORD err) WI_NOEXCEPT + __RFF_CONDITIONAL_METHOD(DWORD, FailFast_IfWin32Error)(__RFF_CONDITIONAL_FN_PARAMS DWORD err) { if (FAILED_WIN32(err)) { @@ -5098,9 +5045,10 @@ __WI_SUPPRESS_4127_E return handle; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) - __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_IfNullAlloc)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_IfNullAlloc)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) { if (pointer == nullptr) { @@ -5109,8 +5057,9 @@ __WI_SUPPRESS_4127_E return pointer; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_IfNullAlloc)(__RFF_CONDITIONAL_FN_PARAMS const PointerT& pointer) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_IfNullAlloc)(__RFF_CONDITIONAL_FN_PARAMS const PointerT& pointer) { if (pointer == nullptr) { @@ -5138,9 +5087,10 @@ __WI_SUPPRESS_4127_E return condition; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) - __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_HrIfNull)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _Pre_maybenull_ PointerT pointer) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_HrIfNull)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _Pre_maybenull_ PointerT pointer) { if (pointer == nullptr) { @@ -5149,8 +5099,9 @@ __WI_SUPPRESS_4127_E return pointer; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_HrIfNull)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_HrIfNull)(__RFF_CONDITIONAL_FN_PARAMS HRESULT hr, _In_opt_ const PointerT& pointer) { if (pointer == nullptr) { @@ -5178,9 +5129,10 @@ __WI_SUPPRESS_4127_E return condition; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) - __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_GetLastErrorIfNull)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_GetLastErrorIfNull)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) { if (pointer == nullptr) { @@ -5189,8 +5141,9 @@ __WI_SUPPRESS_4127_E return pointer; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_GetLastErrorIfNull)(__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_GetLastErrorIfNull)(__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) { if (pointer == nullptr) { @@ -5504,10 +5457,11 @@ __WI_SUPPRESS_4127_E return condition; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> __WI_SUPPRESS_NULLPTR_ANALYSIS _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) - __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_IfNull)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFast_IfNull)(__RFF_CONDITIONAL_FN_PARAMS _Pre_maybenull_ PointerT pointer) { if (pointer == nullptr) { @@ -5516,9 +5470,10 @@ __WI_SUPPRESS_4127_E return pointer; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> __WI_SUPPRESS_NULLPTR_ANALYSIS - __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_IfNull)(__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFast_IfNull)(__RFF_CONDITIONAL_FN_PARAMS _In_opt_ const PointerT& pointer) { if (pointer == nullptr) { @@ -5632,9 +5587,10 @@ __WI_SUPPRESS_4127_E return condition; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_NOT_CLASS(PointerT)> _Post_satisfies_(return == pointer) _When_(pointer == nullptr, _Analysis_noreturn_) - __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFastImmediate_IfNull)(_Pre_maybenull_ PointerT pointer) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(RESULT_NORETURN_NULL PointerT, FailFastImmediate_IfNull)(_Pre_maybenull_ PointerT pointer) { if (pointer == nullptr) { @@ -5643,8 +5599,9 @@ __WI_SUPPRESS_4127_E return pointer; } + // Should be decorated WI_NOEXCEPT, but conflicts with forceinline. template <__RFF_CONDITIONAL_PARTIAL_TEMPLATE typename PointerT, __R_ENABLE_IF_IS_CLASS(PointerT)> - __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFastImmediate_IfNull)(_In_opt_ const PointerT& pointer) WI_NOEXCEPT + __RFF_CONDITIONAL_TEMPLATE_METHOD(void, FailFastImmediate_IfNull)(_In_opt_ const PointerT& pointer) { if (pointer == nullptr) { diff --git a/include/wil/win32_helpers.h b/include/wil/win32_helpers.h index ee1a05c9a..fe0de53eb 100644 --- a/include/wil/win32_helpers.h +++ b/include/wil/win32_helpers.h @@ -36,6 +36,102 @@ #include "wistd_functional.h" #include "wistd_type_traits.h" +#if _HAS_CXX20 && defined(_STRING_VIEW_) && defined(_COMPARE_) +// If we're using c++20, then must be included to use the string ordinal functions +# define __WI_DEFINE_STRING_ORDINAL_FUNCTIONS +#elif !_HAS_CXX20 && defined(_STRING_VIEW_) +# define __WI_DEFINE_STRING_ORDINAL_FUNCTIONS +#endif + +namespace wistd +{ +#if defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS) + +#if _HAS_CXX20 + + using weak_ordering = std::weak_ordering; + +#else // _HAS_CXX20 + + struct weak_ordering + { + static const weak_ordering less; + static const weak_ordering equivalent; + static const weak_ordering greater; + + [[nodiscard]] friend constexpr bool operator==(const weak_ordering left, std::nullptr_t) noexcept + { + return left.m_value == 0; + } + + [[nodiscard]] friend constexpr bool operator!=(const weak_ordering left, std::nullptr_t) noexcept + { + return left.m_value != 0; + } + + [[nodiscard]] friend constexpr bool operator<(const weak_ordering left, std::nullptr_t) noexcept + { + return left.m_value < 0; + } + + [[nodiscard]] friend constexpr bool operator>(const weak_ordering left, std::nullptr_t) noexcept + { + return left.m_value > 0; + } + + [[nodiscard]] friend constexpr bool operator<=(const weak_ordering left, std::nullptr_t) noexcept + { + return left.m_value <= 0; + } + + [[nodiscard]] friend constexpr bool operator>=(const weak_ordering left, std::nullptr_t) noexcept + { + return left.m_value >= 0; + } + + [[nodiscard]] friend constexpr bool operator==(std::nullptr_t, const weak_ordering right) noexcept + { + return right == 0; + } + + [[nodiscard]] friend constexpr bool operator!=(std::nullptr_t, const weak_ordering right) noexcept + { + return right != 0; + } + + [[nodiscard]] friend constexpr bool operator<(std::nullptr_t, const weak_ordering right) noexcept + { + return right > 0; + } + + [[nodiscard]] friend constexpr bool operator>(std::nullptr_t, const weak_ordering right) noexcept + { + return right < 0; + } + + [[nodiscard]] friend constexpr bool operator<=(std::nullptr_t, const weak_ordering right) noexcept + { + return right >= 0; + } + + [[nodiscard]] friend constexpr bool operator>=(std::nullptr_t, const weak_ordering right) noexcept + { + return right <= 0; + } + + signed char m_value; + }; + + inline constexpr weak_ordering weak_ordering::less{static_cast(-1)}; + inline constexpr weak_ordering weak_ordering::equivalent{static_cast(0)}; + inline constexpr weak_ordering weak_ordering::greater{static_cast(1)}; + +#endif // !_HAS_CXX20 + +#endif // defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS) + +} + namespace wil { //! Strictly a function of the file system but this is the value for all known file system, NTFS, FAT. @@ -56,6 +152,41 @@ namespace wil //! For {guid} string form. Not including the null terminator. size_t const guid_string_length = 38; +#pragma region String and identifier comparisons + // Using CompareStringOrdinal functions: + // + // Indentifiers require a locale-less (ordinal), and often case-insensitive, comparison (filenames, registry keys, XML node names, etc). + // DO NOT use locale-sensitive (lexical) comparisons for resource identifiers (e.g.wcs*() functions in the CRT). + +#if defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS) + + namespace details + { + [[nodiscard]] inline int CompareStringOrdinal(std::wstring_view left, std::wstring_view right, bool caseInsensitive) WI_NOEXCEPT + { + // Casting from size_t (unsigned) to int (signed) should be safe from overrun to a negative, + // merely truncating the string. CompareStringOrdinal should be resilient to negatives. + return ::CompareStringOrdinal(left.data(), static_cast(left.size()), right.data(), static_cast(right.size()), caseInsensitive); + } + } + + [[nodiscard]] inline wistd::weak_ordering compare_string_ordinal(std::wstring_view left, std::wstring_view right, bool caseInsensitive) WI_NOEXCEPT + { + switch (wil::details::CompareStringOrdinal(left, right, caseInsensitive)) + { + case CSTR_LESS_THAN: + return wistd::weak_ordering::less; + case CSTR_GREATER_THAN: + return wistd::weak_ordering::greater; + default: + return wistd::weak_ordering::equivalent; + } + } + +#endif // defined(__WI_DEFINE_STRING_ORDINAL_FUNCTIONS) + +#pragma endregion + #pragma region FILETIME helpers // FILETIME duration values. FILETIME is in 100 nanosecond units. namespace filetime_duration