in include/wil/result_macros.h [3463:3593]
inline void LogFailure(__R_FN_PARAMS_FULL, FailureType type, const ResultStatus& resultPair, _In_opt_ PCWSTR message,
bool fWantDebugString, _Out_writes_(debugStringSizeChars) _Post_z_ PWSTR debugString, _Pre_satisfies_(debugStringSizeChars > 0) size_t debugStringSizeChars,
_Out_writes_(callContextStringSizeChars) _Post_z_ PSTR callContextString, _Pre_satisfies_(callContextStringSizeChars > 0) size_t callContextStringSizeChars,
_Out_ FailureInfo *failure) WI_NOEXCEPT
{
debugString[0] = L'\0';
callContextString[0] = L'\0';
static long volatile s_failureId = 0;
failure->hr = resultPair.hr;
failure->status = resultPair.status;
int failureCount = 0;
switch (type)
{
case FailureType::Exception:
failureCount = RecordException(failure->hr);
break;
case FailureType::Return:
failureCount = RecordReturn(failure->hr);
break;
case FailureType::Log:
if (SUCCEEDED(failure->hr))
{
// If you hit this assert (or are reviewing this failure telemetry), then most likely you are trying to log success
// using one of the WIL macros. Example:
// LOG_HR(S_OK);
// Instead, use one of the forms that conditionally logs based upon the error condition:
// LOG_IF_FAILED(hr);
WI_USAGE_ERROR_FORWARD("CALLER BUG: Macro usage error detected. Do not LOG_XXX success.");
failure->hr = __HRESULT_FROM_WIN32(ERROR_ASSERTION_FAILURE);
failure->status = wil::details::HrToNtStatus(failure->hr);
}
failureCount = RecordLog(failure->hr);
break;
case FailureType::FailFast:
failureCount = RecordFailFast(failure->hr);
break;
};
failure->type = type;
failure->flags = FailureFlags::None;
WI_SetFlagIf(failure->flags, FailureFlags::NtStatus, resultPair.isNtStatus);
failure->failureId = ::InterlockedIncrementNoFence(&s_failureId);
failure->pszMessage = ((message != nullptr) && (message[0] != L'\0')) ? message : nullptr;
failure->threadId = ::GetCurrentThreadId();
failure->pszFile = fileName;
failure->uLineNumber = lineNumber;
failure->cFailureCount = failureCount;
failure->pszCode = code;
failure->pszFunction = functionName;
failure->returnAddress = returnAddress;
failure->callerReturnAddress = callerReturnAddress;
failure->pszCallContext = nullptr;
::ZeroMemory(&failure->callContextCurrent, sizeof(failure->callContextCurrent));
::ZeroMemory(&failure->callContextOriginating, sizeof(failure->callContextOriginating));
failure->pszModule = (g_pfnGetModuleName != nullptr) ? g_pfnGetModuleName() : nullptr;
// Completes filling out failure, notifies thread-based callbacks and the telemetry callback
if (details::g_pfnGetContextAndNotifyFailure)
{
details::g_pfnGetContextAndNotifyFailure(failure, callContextString, callContextStringSizeChars);
}
// Allow hooks to inspect the failure before acting upon it
if (details::g_pfnLoggingCallback)
{
details::g_pfnLoggingCallback(*failure);
}
// If the hook is enabled then it will be given the opportunity to call RoOriginateError to greatly improve the diagnostic experience
// for uncaught exceptions. In cases where we will be throwing a C++/CX Platform::Exception we should avoid originating because the
// CX runtime will be doing that for us. fWantDebugString is only set to true when the caller will be throwing a Platform::Exception.
if (details::g_pfnOriginateCallback && !fWantDebugString)
{
details::g_pfnOriginateCallback(*failure);
}
if (SUCCEEDED(failure->hr))
{
// Caller bug: Leaking a success code into a failure-only function
FAIL_FAST_IMMEDIATE_IF(type != FailureType::FailFast);
failure->hr = E_UNEXPECTED;
failure->status = wil::details::HrToNtStatus(failure->hr);
}
bool const fUseOutputDebugString = IsDebuggerPresent() && g_fResultOutputDebugString;
// We need to generate the logging message if:
// * We're logging to OutputDebugString
// * OR the caller asked us to (generally for attaching to a C++/CX exception)
if (fWantDebugString || fUseOutputDebugString)
{
// Call the logging callback (if present) to allow them to generate the debug string that will be pushed to the console
// or the platform exception object if the caller desires it.
if ((g_pfnResultLoggingCallback != nullptr) && !g_resultMessageCallbackSet)
{
g_pfnResultLoggingCallback(failure, debugString, debugStringSizeChars);
}
// The callback only optionally needs to supply the debug string -- if the callback didn't populate it, yet we still want
// it for OutputDebugString or exception message, then generate the default string.
if (debugString[0] == L'\0')
{
GetFailureLogString(debugString, debugStringSizeChars, *failure);
}
if (fUseOutputDebugString)
{
::OutputDebugStringW(debugString);
}
}
else
{
// [deprecated behavior]
// This callback was at one point *always* called for all failures, so we continue to call it for failures even when we don't
// need to generate the debug string information (when the callback was supplied directly). We can avoid this if the caller
// used the explicit function (through g_resultMessageCallbackSet)
if ((g_pfnResultLoggingCallback != nullptr) && !g_resultMessageCallbackSet)
{
g_pfnResultLoggingCallback(failure, nullptr, 0);
}
}
if (g_fBreakOnFailure && (g_pfnDebugBreak != nullptr))
{
g_pfnDebugBreak();
}
}