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.
 
 
 
 
 
 

128 line
6.8 KiB

  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. // Note: When origination is enabled by including this file, origination is done as part of the RETURN_* and THROW_* macros. Before originating
  12. // 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,
  13. // 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
  14. // per-thread error information. The act of checking the error clears it, so there should be minimal risk of failing to originate distinct errors
  15. // simply because the HRESULTs match.
  16. //
  17. // For THROW_ macros we will examine the thread-local error storage once per throw. So typically once, with additional calls if the exception is
  18. // caught and re-thrown.
  19. //
  20. // For RETURN_ macros we will have to examine the thread-local error storage once per frame as the call stack unwinds. Because error conditions
  21. // -should- be uncommon the performance impact of checking TLS should be minimal. The more expensive part is originating the error because it must
  22. // capture the entire stack and some additional data.
  23. #ifndef __WIL_RESULT_ORIGINATE_INCLUDED
  24. #define __WIL_RESULT_ORIGINATE_INCLUDED
  25. #include "result.h"
  26. #include <OleAuto.h> // RestrictedErrorInfo uses BSTRs :(
  27. #include "resource.h"
  28. #include "com.h"
  29. #include <roerrorapi.h>
  30. #ifndef __cplusplus_winrt // The CX runtime likes to originate errors already so we would conflict with them.
  31. namespace wil
  32. {
  33. namespace details
  34. {
  35. // Note: The name must begin with "Raise" so that the !analyze auto-bucketing will ignore this stack frame. Otherwise this line of code gets all the blame.
  36. inline void __stdcall RaiseRoOriginateOnWilExceptions(wil::FailureInfo const& failure) WI_NOEXCEPT
  37. {
  38. if ((failure.type == FailureType::Return) || (failure.type == FailureType::Exception))
  39. {
  40. bool shouldOriginate = true;
  41. wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
  42. if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK)
  43. {
  44. // This thread already has an error origination payload. Don't originate again if it has the same HRESULT that we are
  45. // observing right now.
  46. wil::unique_bstr descriptionUnused;
  47. HRESULT existingHr = failure.hr;
  48. wil::unique_bstr restrictedDescriptionUnused;
  49. wil::unique_bstr capabilitySidUnused;
  50. if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)))
  51. {
  52. shouldOriginate = (failure.hr != existingHr);
  53. }
  54. }
  55. if (shouldOriginate)
  56. {
  57. #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
  58. wil::unique_hmodule errorModule;
  59. if (GetModuleHandleExW(0, L"api-ms-win-core-winrt-error-l1-1-1.dll", &errorModule))
  60. {
  61. auto pfn = reinterpret_cast<decltype(&::RoOriginateError)>(GetProcAddress(errorModule.get(), "RoOriginateError"));
  62. if (pfn != nullptr)
  63. {
  64. pfn(failure.hr, nullptr);
  65. }
  66. }
  67. #else // DESKTOP | SYSTEM
  68. ::RoOriginateError(failure.hr, nullptr);
  69. #endif // DESKTOP | SYSTEM
  70. }
  71. else if (restrictedErrorInformation)
  72. {
  73. // GetRestrictedErrorInfo returns ownership of the error information. If we aren't originating, and an error was already present,
  74. // then we need to restore the error information for later observation.
  75. SetRestrictedErrorInfo(restrictedErrorInformation.get());
  76. }
  77. }
  78. }
  79. // This method will check for the presence of stowed exception data on the current thread. If such data exists, and the HRESULT
  80. // matches the current failure, then we will call RoFailFastWithErrorContext. RoFailFastWithErrorContext in this situation will
  81. // result in -VASTLY- improved crash bucketing. It is hard to express just how much better. In other cases we just return and
  82. // the calling method fails fast the same way it always has.
  83. inline void __stdcall FailfastWithContextCallback(wil::FailureInfo const& failure) WI_NOEXCEPT
  84. {
  85. wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
  86. if (GetRestrictedErrorInfo(&restrictedErrorInformation) == S_OK)
  87. {
  88. wil::unique_bstr descriptionUnused;
  89. HRESULT existingHr = failure.hr;
  90. wil::unique_bstr restrictedDescriptionUnused;
  91. wil::unique_bstr capabilitySidUnused;
  92. if (SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused)) &&
  93. (existingHr == failure.hr))
  94. {
  95. // GetRestrictedErrorInfo returns ownership of the error information. We want it to be available for RoFailFastWithErrorContext
  96. // so we must restore it via SetRestrictedErrorInfo first.
  97. SetRestrictedErrorInfo(restrictedErrorInformation.get());
  98. RoFailFastWithErrorContext(existingHr);
  99. }
  100. else
  101. {
  102. // The error didn't match the current failure. Put it back in thread-local storage even though we aren't failing fast
  103. // in this method, so it is available in the debugger just-in-case.
  104. SetRestrictedErrorInfo(restrictedErrorInformation.get());
  105. }
  106. }
  107. }
  108. } // namespace details
  109. } // namespace wil
  110. // Automatically call RoOriginateError upon error origination by including this file
  111. WI_HEADER_INITITALIZATION_FUNCTION(ResultStowedExceptionInitialize, []
  112. {
  113. ::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions);
  114. ::wil::SetFailfastWithContextCallback(::wil::details::FailfastWithContextCallback);
  115. return 1;
  116. });
  117. #endif // __cplusplus_winrt
  118. #endif // __WIL_RESULT_ORIGINATE_INCLUDED