You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

252 lines
9.1KB

  1. //*********************************************************
  2. //
  3. // Copyright (c) Microsoft. All rights reserved.
  4. // This code is licensed under the MIT License.
  5. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
  6. // ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  7. // TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  8. // PARTICULAR PURPOSE AND NONINFRINGEMENT.
  9. //
  10. //*********************************************************
  11. #ifndef __WIL_CPPWINRT_INCLUDED
  12. #define __WIL_CPPWINRT_INCLUDED
  13. #include "common.h"
  14. #include <windows.h>
  15. #include <unknwn.h>
  16. #include <hstring.h>
  17. // WIL and C++/WinRT use two different exception types for communicating HRESULT failures. Thus, both libraries need to
  18. // understand how to translate these exception types into the correct HRESULT values at the ABI boundary. Prior to
  19. // C++/WinRT "2.0" this was accomplished by injecting the WINRT_EXTERNAL_CATCH_CLAUSE macro - that WIL defines below -
  20. // into its exception handler (winrt::to_hresult). Starting with C++/WinRT "2.0" this mechanism has shifted to a global
  21. // function pointer - winrt_to_hresult_handler - that WIL sets automatically when this header is included and
  22. // 'CPPWINRT_SUPPRESS_STATIC_INITIALIZERS' is not defined.
  23. /// @cond
  24. namespace wil::details
  25. {
  26. // Since the C++/WinRT version macro is a string...
  27. inline constexpr int major_version_from_string(const char* versionString)
  28. {
  29. int result = 0;
  30. auto str = versionString;
  31. while ((*str >= '0') && (*str <= '9'))
  32. {
  33. result = result * 10 + (*str - '0');
  34. ++str;
  35. }
  36. return result;
  37. }
  38. }
  39. /// @endcond
  40. #ifdef CPPWINRT_VERSION
  41. // Prior to C++/WinRT "2.0" this header needed to be included before 'winrt/base.h' so that our definition of
  42. // 'WINRT_EXTERNAL_CATCH_CLAUSE' would get picked up in the implementation of 'winrt::to_hresult'. This is no longer
  43. // problematic, so only emit an error when using a version of C++/WinRT prior to 2.0
  44. static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2,
  45. "Please include wil/cppwinrt.h before including any C++/WinRT headers");
  46. #endif
  47. // NOTE: Will eventually be removed once C++/WinRT 2.0 use can be assumed
  48. #ifdef WINRT_EXTERNAL_CATCH_CLAUSE
  49. #define __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE 1
  50. #else
  51. #define WINRT_EXTERNAL_CATCH_CLAUSE \
  52. catch (const wil::ResultException& e) \
  53. { \
  54. return winrt::hresult_error(e.GetErrorCode(), winrt::to_hstring(e.what())).to_abi(); \
  55. }
  56. #endif
  57. #include "result_macros.h"
  58. #include <winrt/base.h>
  59. #if __WI_CONFLICTING_WINRT_EXTERNAL_CATCH_CLAUSE
  60. static_assert(::wil::details::major_version_from_string(CPPWINRT_VERSION) >= 2,
  61. "C++/WinRT external catch clause already defined outside of WIL");
  62. #endif
  63. // In C++/WinRT 2.0 and beyond, this function pointer exists. In earlier versions it does not. It's much easier to avoid
  64. // linker errors than it is to SFINAE on variable existence, so we declare the variable here, but are careful not to
  65. // use it unless the version of C++/WinRT is high enough
  66. extern std::int32_t(__stdcall* winrt_to_hresult_handler)(void*) noexcept;
  67. /// @cond
  68. namespace wil::details
  69. {
  70. inline void MaybeGetExceptionString(
  71. const winrt::hresult_error& exception,
  72. _Out_writes_opt_(debugStringChars) PWSTR debugString,
  73. _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars)
  74. {
  75. if (debugString)
  76. {
  77. StringCchPrintfW(debugString, debugStringChars, L"winrt::hresult_error: %ls", exception.message().c_str());
  78. }
  79. }
  80. inline HRESULT __stdcall ResultFromCaughtException_CppWinRt(
  81. _Inout_updates_opt_(debugStringChars) PWSTR debugString,
  82. _When_(debugString != nullptr, _Pre_satisfies_(debugStringChars > 0)) size_t debugStringChars,
  83. _Inout_ bool* isNormalized) noexcept
  84. {
  85. if (g_pfnResultFromCaughtException)
  86. {
  87. try
  88. {
  89. throw;
  90. }
  91. catch (const ResultException& exception)
  92. {
  93. *isNormalized = true;
  94. MaybeGetExceptionString(exception, debugString, debugStringChars);
  95. return exception.GetErrorCode();
  96. }
  97. catch (const winrt::hresult_error& exception)
  98. {
  99. MaybeGetExceptionString(exception, debugString, debugStringChars);
  100. return exception.code().value;
  101. }
  102. catch (const std::bad_alloc& exception)
  103. {
  104. MaybeGetExceptionString(exception, debugString, debugStringChars);
  105. return E_OUTOFMEMORY;
  106. }
  107. catch (const std::out_of_range& exception)
  108. {
  109. MaybeGetExceptionString(exception, debugString, debugStringChars);
  110. return E_BOUNDS;
  111. }
  112. catch (const std::invalid_argument& exception)
  113. {
  114. MaybeGetExceptionString(exception, debugString, debugStringChars);
  115. return E_INVALIDARG;
  116. }
  117. catch (...)
  118. {
  119. auto hr = RecognizeCaughtExceptionFromCallback(debugString, debugStringChars);
  120. if (FAILED(hr))
  121. {
  122. return hr;
  123. }
  124. }
  125. }
  126. else
  127. {
  128. try
  129. {
  130. throw;
  131. }
  132. catch (const ResultException& exception)
  133. {
  134. *isNormalized = true;
  135. MaybeGetExceptionString(exception, debugString, debugStringChars);
  136. return exception.GetErrorCode();
  137. }
  138. catch (const winrt::hresult_error& exception)
  139. {
  140. MaybeGetExceptionString(exception, debugString, debugStringChars);
  141. return exception.code().value;
  142. }
  143. catch (const std::bad_alloc& exception)
  144. {
  145. MaybeGetExceptionString(exception, debugString, debugStringChars);
  146. return E_OUTOFMEMORY;
  147. }
  148. catch (const std::out_of_range& exception)
  149. {
  150. MaybeGetExceptionString(exception, debugString, debugStringChars);
  151. return E_BOUNDS;
  152. }
  153. catch (const std::invalid_argument& exception)
  154. {
  155. MaybeGetExceptionString(exception, debugString, debugStringChars);
  156. return E_INVALIDARG;
  157. }
  158. catch (const std::exception& exception)
  159. {
  160. MaybeGetExceptionString(exception, debugString, debugStringChars);
  161. return HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION);
  162. }
  163. catch (...)
  164. {
  165. // Fall through to returning 'S_OK' below
  166. }
  167. }
  168. // Tell the caller that we were unable to map the exception by succeeding...
  169. return S_OK;
  170. }
  171. }
  172. /// @endcond
  173. namespace wil
  174. {
  175. inline std::int32_t __stdcall winrt_to_hresult(void* returnAddress) noexcept
  176. {
  177. // C++/WinRT only gives us the return address (caller), so pass along an empty 'DiagnosticsInfo' since we don't
  178. // have accurate file/line/etc. information
  179. return static_cast<std::int32_t>(details::ReportFailure_CaughtException<FailureType::Return>(__R_DIAGNOSTICS_RA(DiagnosticsInfo{}, returnAddress)));
  180. }
  181. inline void WilInitialize_CppWinRT()
  182. {
  183. details::g_pfnResultFromCaughtException_CppWinRt = details::ResultFromCaughtException_CppWinRt;
  184. if constexpr (details::major_version_from_string(CPPWINRT_VERSION) >= 2)
  185. {
  186. WI_ASSERT(winrt_to_hresult_handler == nullptr);
  187. winrt_to_hresult_handler = winrt_to_hresult;
  188. }
  189. }
  190. /// @cond
  191. namespace details
  192. {
  193. #ifndef CPPWINRT_SUPPRESS_STATIC_INITIALIZERS
  194. WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "0")
  195. WI_HEADER_INITITALIZATION_FUNCTION(WilInitialize_CppWinRT, []
  196. {
  197. ::wil::WilInitialize_CppWinRT();
  198. return 1;
  199. });
  200. #else
  201. WI_ODR_PRAGMA("CPPWINRT_SUPPRESS_STATIC_INITIALIZERS", "1")
  202. #endif
  203. }
  204. /// @endcond
  205. // Provides an overload of verify_hresult so that the WIL macros can recognize winrt::hresult as a valid "hresult" type.
  206. inline long verify_hresult(winrt::hresult hr) noexcept
  207. {
  208. return hr;
  209. }
  210. // Provides versions of get_abi and put_abi for genericity that directly use HSTRING for convenience.
  211. template <typename T>
  212. auto get_abi(T const& object) noexcept
  213. {
  214. return winrt::get_abi(object);
  215. }
  216. inline auto get_abi(winrt::hstring const& object) noexcept
  217. {
  218. return static_cast<HSTRING>(winrt::get_abi(object));
  219. }
  220. template <typename T>
  221. auto put_abi(T& object) noexcept
  222. {
  223. return winrt::put_abi(object);
  224. }
  225. inline auto put_abi(winrt::hstring& object) noexcept
  226. {
  227. return reinterpret_cast<HSTRING*>(winrt::put_abi(object));
  228. }
  229. }
  230. #endif // __WIL_CPPWINRT_INCLUDED