//********************************************************* // // Copyright (c) Microsoft. All rights reserved. // This code is licensed under the MIT License. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A // PARTICULAR PURPOSE AND NONINFRINGEMENT. // //********************************************************* #ifndef __WIL_CPPWINRT_INCLUDED #define __WIL_CPPWINRT_INCLUDED #include "common.h" #include #include #include // WIL and C++/WinRT use two different exception types for communicating HRESULT failures. Thus, both libraries need to // understand how to translate these exception types into the correct HRESULT values at the ABI boundary. Prior to // C++/WinRT "2.0" this was accomplished by injecting the WINRT_EXTERNAL_CATCH_CLAUSE macro - that WIL defines below - // into its exception handler (winrt::to_hresult). Starting with C++/WinRT "2.0" this mechanism has shifted to a global // function pointer - winrt_to_hresult_handler - that WIL sets automatically when this header is included and // 'CPPWINRT_SUPPRESS_STATIC_INITIALIZERS' is not defined. /// @cond namespace wil::details { // Since the C++/WinRT version macro is a string... inline constexpr int major_version_from_string(const char* versionString) { int result = 0; auto str = versionString; while ((*str >= '0') && (*str <= '9')) { result = result * 10 + (*str - '0'); ++str; } return result; } } /// @endcond #ifdef CPPWINRT_VERSION // Prior to C++/WinRT "2.0" this header needed to be included before 'winrt/base.h' so that our definition of // 'WINRT_EXTERNAL_CATCH_CLAUSE' would get picked up in the implementation of 'winrt::to_hresult'. This is no longer // problematic, so only emit an error when using a version of C++/WinRT prior to 2.0 static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2, "Please include wil/cppwinrt.h before including any C++/WinRT headers"); #endif // NOTE: Will eventually be removed once C++/WinRT 2.0 use can be assumed #ifdef WINRT_EXTERNAL_CATCH_CLAUSE #define __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE 1 #else #define WINRT_EXTERNAL_CATCH_CLAUSE \ catch (const wil::ResultException& e) \ { \ return winrt::hresult_error(e.GetErrorCode(), winrt::to_hstring(e.what())).to_abi(); \ } #endif #include "result_macros.h" #include #if __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2, "C++/WinRT external catch clause already defined outside of WIL"); #endif // In C++/WinRT 2.0 and beyond, this function pointer exists. In earlier versions it does not. It's much easier to avoid // linker errors than it is to SFINAE on variable existence, so we declare the variable here, but are careful not to // use it unless the version of C++/WinRT is high enough extern std::int32_t(__stdcall* winrt_to_hresult_handler)(void*) noexcept; /// @cond namespace wil::details { inline void MaybeGetExceptionString( const winrt::hresult_error& exception, _Out_writes_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars) { if (debugString) { StringCchPrintfW(debugString, debugStringChars, L"winrt::hresult_error: %ls", exception.message().c_str()); } } inline HRESULT __stdcall ResultFromCaughtException_CppWinRt( _Inout_updates_opt_(debugStringChars) PWSTR debugString, _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars, _Inout_ bool* isNormalized) noexcept { if (g_pfnResultFromCaughtException) { try { throw; } catch (const ResultException& exception) { *isNormalized = true; MaybeGetExceptionString(exception, debugString, debugStringChars); return exception.GetErrorCode(); } catch (const winrt::hresult_error& exception) { MaybeGetExceptionString(exception, debugString, debugStringChars); return exception.code().value; } catch (const std::bad_alloc& exception) { MaybeGetExceptionString(exception, debugString, debugStringChars); return E_OUTOFMEMORY; } catch (const std::out_of_range& exception) { MaybeGetExceptionString(exception, debugString, debugStringChars); return E_BOUNDS; } catch (const std::invalid_argument& exception) { MaybeGetExceptionString(exception, debugString, debugStringChars); return E_INVALIDARG; } catch (...) { auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars); if (FAILED(hr)) { return hr; } } } else { try { throw; } catch (const ResultException& exception) { *isNormalized = true; MaybeGetExceptionString(exception, debugString, debugStringChars); return exception.GetErrorCode(); } catch (const winrt::hresult_error& exception) { MaybeGetExceptionString(exception, debugString, debugStringChars); return exception.code().value; } catch (const std::bad_alloc& exception) { MaybeGetExceptionString(exception, debugString, debugStringChars); return E_OUTOFMEMORY; } catch (const std::out_of_range& exception) { MaybeGetExceptionString(exception, debugString, debugStringChars); return E_BOUNDS; } catch (const std::invalid_argument& exception) { MaybeGetExceptionString(exception, debugString, debugStringChars); return E_INVALIDARG; } catch (const std::exception& exception) { MaybeGetExceptionString(exception, debugString, debugStringChars); return HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION); } catch (...) { // Fall through to returning 'S_OK' below } } // Tell the caller that we were unable to map the exception by succeeding... return S_OK; } } /// @endcond namespace wil { inline std::int32_t __stdcall winrt_to_hresult(void* returnAddress) noexcept { // C++/WinRT only gives us the return address (caller), so pass along an empty 'DiagnosticsInfo' since we don't // have accurate file/line/etc. information return static_cast(details::ReportFailure_CaughtException(__R_DIAGNOSTICS_RA(DiagnosticsInfo{}, returnAddress))); } inline void WilInitialize_CppWinRT() { details::g_pfnResultFromCaughtException_CppWinRt = details::ResultFromCaughtException_CppWinRt; if constexpr (details::major_version_from_string(CPPWINRT_VERSION) >= 2) { WI_ASSERT(winrt_to_hresult_handler == nullptr); winrt_to_hresult_handler = winrt_to_hresult; } } /// @cond namespace details { #ifndef CPPWINRT_SUPPRESS_STATIC_INITIALIZERS WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "0") WI_HEADER_INITITALIZATION_FUNCTION(WilInitialize_CppWinRT, [] { ::wil::WilInitialize_CppWinRT(); return 1; }); #else WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "1") #endif } /// @endcond // Provides an overload of verify_hresult so that the WIL macros can recognize winrt::hresult as a valid "hresult" type. inline long verify_hresult(winrt::hresult hr) noexcept { return hr; } // Provides versions of get_abi and put_abi for genericity that directly use HSTRING for convenience. template auto get_abi(T const& object) noexcept { return winrt::get_abi(object); } inline auto get_abi(winrt::hstring const& object) noexcept { return static_cast(winrt::get_abi(object)); } template auto put_abi(T& object) noexcept { return winrt::put_abi(object); } inline auto put_abi(winrt::hstring& object) noexcept { return reinterpret_cast(winrt::put_abi(object)); } } #endif // __WIL_CPPWINRT_INCLUDED