// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. //***************************************************************************** // PostErrors.cpp // // This module contains the error handling/posting code for the engine. It // is assumed that all methods may be called by a dispatch client, and therefore // errors are always posted using IErrorInfo. // //***************************************************************************** #include "stdafx.h" // Standard header. #ifndef FEATURE_UTILCODE_NO_DEPENDENCIES #include // Utility helpers. #include #include "../dlls/mscorrc/resource.h" #include "ex.h" #include #if !defined(lengthof) #define lengthof(x) (sizeof(x)/sizeof(x[0])) #endif // Local prototypes. HRESULT FillErrorInfo(LPCWSTR szMsg, DWORD dwHelpContext); //***************************************************************************** // Function that we'll expose to the outside world to fire off the shutdown method //***************************************************************************** #ifdef SHOULD_WE_CLEANUP void ShutdownCompRC() { CCompRC::ShutdownDefaultResourceDll(); } #endif /* SHOULD_WE_CLEANUP */ void GetResourceCultureCallbacks( FPGETTHREADUICULTURENAMES* fpGetThreadUICultureNames, FPGETTHREADUICULTUREID* fpGetThreadUICultureId) { WRAPPER_NO_CONTRACT; CCompRC::GetDefaultCallbacks( fpGetThreadUICultureNames, fpGetThreadUICultureId ); } //***************************************************************************** // Set callbacks to get culture info //***************************************************************************** void SetResourceCultureCallbacks( FPGETTHREADUICULTURENAMES fpGetThreadUICultureNames, FPGETTHREADUICULTUREID fpGetThreadUICultureId // TODO: Don't rely on the LCID, only the name ) { WRAPPER_NO_CONTRACT; CCompRC::SetDefaultCallbacks( fpGetThreadUICultureNames, fpGetThreadUICultureId ); } //***************************************************************************** // Public function to load a resource string //***************************************************************************** STDAPI UtilLoadStringRC( UINT iResourceID, __out_ecount(iMax) LPWSTR szBuffer, int iMax, int bQuiet ) { WRAPPER_NO_CONTRACT; return UtilLoadResourceString(bQuiet? CCompRC::Optional : CCompRC::Required,iResourceID, szBuffer, iMax); } HRESULT UtilLoadResourceString(CCompRC::ResourceCategory eCategory, UINT iResourceID, __out_ecount (iMax) LPWSTR szBuffer, int iMax) { CONTRACTL { DISABLED(NOTHROW); GC_NOTRIGGER; SO_TOLERANT; } CONTRACTL_END; HRESULT retVal = E_OUTOFMEMORY; BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); SString::Startup(); EX_TRY { CCompRC *pResourceDLL = CCompRC::GetDefaultResourceDll(); if (pResourceDLL != NULL) { retVal = pResourceDLL->LoadString(eCategory, iResourceID, szBuffer, iMax); } } EX_CATCH { // Catch any errors and return E_OUTOFMEMORY; retVal = E_OUTOFMEMORY; } EX_END_CATCH(SwallowAllExceptions); END_SO_INTOLERANT_CODE; return retVal; } #ifdef FEATURE_USE_LCID STDAPI UtilLoadStringRCEx( LCID lcid, UINT iResourceID, __out_ecount(iMax) LPWSTR szBuffer, int iMax, int bQuiet, int *pcwchUsed ) { CONTRACTL { DISABLED(NOTHROW); GC_NOTRIGGER; SO_TOLERANT; } CONTRACTL_END; HRESULT retVal = E_OUTOFMEMORY; BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW); EX_TRY { SString::Startup(); CCompRC *pResourceDLL = CCompRC::GetDefaultResourceDll(); if (pResourceDLL != NULL) { retVal = pResourceDLL->LoadString(bQuiet? CCompRC::Optional : CCompRC::Required,lcid, iResourceID, szBuffer, iMax, pcwchUsed); } } EX_CATCH { // Catch any errors and return E_OUTOFMEMORY; retVal = E_OUTOFMEMORY; } EX_END_CATCH(SwallowAllExceptions); END_SO_INTOLERANT_CODE; return retVal; } #endif //FEATURE_USE_LCID //***************************************************************************** // Format a Runtime Error message. //***************************************************************************** HRESULT __cdecl FormatRuntimeErrorVa( __inout_ecount(cchMsg) WCHAR *rcMsg, // Buffer into which to format. ULONG cchMsg, // Size of buffer, characters. HRESULT hrRpt, // The HR to report. va_list marker) // Optional args. { CONTRACTL { NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; WCHAR rcBuf[512]; // Resource string. HRESULT hr; // Ensure nul termination. *rcMsg = W('\0'); // If this is one of our errors or if it is simply a resource ID, then grab the error from the rc file. if ((HRESULT_FACILITY(hrRpt) == FACILITY_URT) || (HIWORD(hrRpt) == 0)) { hr = UtilLoadStringRC(LOWORD(hrRpt), rcBuf, NumItems(rcBuf), true); if (hr == S_OK) { _vsnwprintf_s(rcMsg, cchMsg, _TRUNCATE, rcBuf, marker); } } // Otherwise it isn't one of ours, so we need to see if the system can // find the text for it. else { #ifdef FEATURE_USE_LCID if (WszFormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, hrRpt, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), rcMsg, cchMsg, 0/*@todo: marker*/)) #else if (WszFormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 0, hrRpt, 0, rcMsg, cchMsg, 0/*@todo: marker*/)) #endif { hr = S_OK; // System messages contain a trailing \r\n, which we don't want normally. size_t iLen = wcslen(rcMsg); if (iLen > 3 && rcMsg[iLen - 2] == '\r' && rcMsg[iLen - 1] == '\n') rcMsg[iLen - 2] = '\0'; } else hr = HRESULT_FROM_GetLastError(); } // If we failed to find the message anywhere, then issue a hard coded message. if (FAILED(hr)) { _snwprintf_s(rcMsg, cchMsg, _TRUNCATE, W("Common Language Runtime Internal error: 0x%08x"), hrRpt); DEBUG_STMT(DbgWriteEx(rcMsg)); } return hrRpt; } // FormatRuntimeErrorVa //***************************************************************************** // Format a Runtime Error message, varargs. //***************************************************************************** HRESULT __cdecl FormatRuntimeError( __out_ecount(cchMsg) WCHAR *rcMsg, // Buffer into which to format. ULONG cchMsg, // Size of buffer, characters. HRESULT hrRpt, // The HR to report. ...) // Optional args. { WRAPPER_NO_CONTRACT; va_list marker; // User text. va_start(marker, hrRpt); hrRpt = FormatRuntimeErrorVa(rcMsg, cchMsg, hrRpt, marker); va_end(marker); return hrRpt; } #ifdef FEATURE_COMINTEROP //***************************************************************************** // Create, fill out and set an error info object. Note that this does not fill // out the IID for the error object; that is done elsewhere. //***************************************************************************** HRESULT FillErrorInfo( // Return status. LPCWSTR szMsg, // Error message. DWORD dwHelpContext) // Help context. { CONTRACTL { NOTHROW; GC_NOTRIGGER; } CONTRACTL_END; ICreateErrorInfo *pICreateErr = NULL; // Error info creation Iface pointer. IErrorInfo *pIErrInfo = NULL; // The IErrorInfo interface. HRESULT hr; // Return status. // Get the ICreateErrorInfo pointer. hr = S_OK; EX_TRY { hr = CreateErrorInfo(&pICreateErr); } EX_CATCH { hr = GET_EXCEPTION()->GetHR(); } EX_END_CATCH(SwallowAllExceptions); if (FAILED(hr)) return (hr); // Set message text description. if (FAILED(hr = pICreateErr->SetDescription((LPWSTR) szMsg))) goto Exit1; // suppress PreFast warning about passing literal string to non-const API. // This API (ICreateErrorInfo::SetHelpFile) is documented to take a const argument, but // we can't put const in the signature because it would break existing implementors of // the API. #ifdef _PREFAST_ #pragma prefast(push) #pragma warning(disable:6298) #endif // Set the help file and help context. //@todo: we don't have a help file yet. if (FAILED(hr = pICreateErr->SetHelpFile(const_cast(W("complib.hlp")))) || FAILED(hr = pICreateErr->SetHelpContext(dwHelpContext))) goto Exit1; #ifdef _PREFAST_ #pragma prefast(pop) #endif // Get the IErrorInfo pointer. if (FAILED(hr = pICreateErr->QueryInterface(IID_IErrorInfo, (PVOID *) &pIErrInfo))) goto Exit1; // Save the error and release our local pointers. { // If we get here, we have loaded oleaut32.dll. CONTRACT_VIOLATION(ThrowsViolation); SetErrorInfo(0L, pIErrInfo); } Exit1: pICreateErr->Release(); if (pIErrInfo) { pIErrInfo->Release(); } return hr; } #endif // FEATURE_COMINTEROP //***************************************************************************** // This function will post an error for the client. If the LOWORD(hrRpt) can // be found as a valid error message, then it is loaded and formatted with // the arguments passed in. If it cannot be found, then the error is checked // against FormatMessage to see if it is a system error. System errors are // not formatted so no add'l parameters are required. If any errors in this // process occur, hrRpt is returned for the client with no error posted. //***************************************************************************** extern "C" HRESULT __cdecl PostErrorVA( // Returned error. HRESULT hrRpt, // Reported error. va_list marker) // Error arguments. { CONTRACTL { NOTHROW; GC_NOTRIGGER; ENTRY_POINT; } CONTRACTL_END; #ifdef FEATURE_COMINTEROP const DWORD cchMsg = 4096; WCHAR *rcMsg = (WCHAR*)alloca(cchMsg * sizeof(WCHAR)); // Error message. HRESULT hr; BEGIN_ENTRYPOINT_NOTHROW; // Return warnings without text. if (!FAILED(hrRpt)) goto ErrExit; // If we are already out of memory or out of stack or the thread is in some bad state, // we don't want throw gasoline on the fire by calling ErrorInfo stuff below (which can // trigger a delayload of oleaut32.dll). We don't need to embellish transient errors // so just return this without text. if (Exception::IsTransient(hrRpt)) { goto ErrExit; } // Format the error. FormatRuntimeErrorVa(rcMsg, cchMsg, hrRpt, marker); // Turn the error into a posted error message. If this fails, we still // return the original error message since a message caused by our error // handling system isn't going to give you a clue about the original error. hr = FillErrorInfo(rcMsg, LOWORD(hrRpt)); _ASSERTE(hr == S_OK); ErrExit: END_ENTRYPOINT_NOTHROW; #endif // FEATURE_COMINTEROP return (hrRpt); } // PostErrorVA #endif //!FEATURE_UTILCODE_NO_DEPENDENCIES //***************************************************************************** // This function will post an error for the client. If the LOWORD(hrRpt) can // be found as a valid error message, then it is loaded and formatted with // the arguments passed in. If it cannot be found, then the error is checked // against FormatMessage to see if it is a system error. System errors are // not formatted so no add'l parameters are required. If any errors in this // process occur, hrRpt is returned for the client with no error posted. //***************************************************************************** extern "C" HRESULT __cdecl PostError( HRESULT hrRpt, // Reported error. ...) // Error arguments. { #ifndef FEATURE_UTILCODE_NO_DEPENDENCIES WRAPPER_NO_CONTRACT; va_list marker; // User text. va_start(marker, hrRpt); hrRpt = PostErrorVA(hrRpt, marker); va_end(marker); #endif //!FEATURE_UTILCODE_NO_DEPENDENCIES return hrRpt; }