1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//*****************************************************************************
5// PostErrors.cpp
6//
7// This module contains the error handling/posting code for the engine. It
8// is assumed that all methods may be called by a dispatch client, and therefore
9// errors are always posted using IErrorInfo.
10//
11
12//*****************************************************************************
13#include "stdafx.h" // Standard header.
14
15#ifndef FEATURE_UTILCODE_NO_DEPENDENCIES
16
17#include <utilcode.h> // Utility helpers.
18#include <corerror.h>
19#include "../dlls/mscorrc/resource.h"
20#include "ex.h"
21
22#include <posterror.h>
23
24#if !defined(lengthof)
25#define lengthof(x) (sizeof(x)/sizeof(x[0]))
26#endif
27
28// Local prototypes.
29HRESULT FillErrorInfo(LPCWSTR szMsg, DWORD dwHelpContext);
30
31//*****************************************************************************
32// Function that we'll expose to the outside world to fire off the shutdown method
33//*****************************************************************************
34#ifdef SHOULD_WE_CLEANUP
35void ShutdownCompRC()
36{
37 CCompRC::ShutdownDefaultResourceDll();
38}
39#endif /* SHOULD_WE_CLEANUP */
40
41void GetResourceCultureCallbacks(
42 FPGETTHREADUICULTURENAMES* fpGetThreadUICultureNames,
43 FPGETTHREADUICULTUREID* fpGetThreadUICultureId)
44{
45 WRAPPER_NO_CONTRACT;
46 CCompRC::GetDefaultCallbacks(
47 fpGetThreadUICultureNames,
48 fpGetThreadUICultureId
49 );
50}
51//*****************************************************************************
52// Set callbacks to get culture info
53//*****************************************************************************
54void SetResourceCultureCallbacks(
55 FPGETTHREADUICULTURENAMES fpGetThreadUICultureNames,
56 FPGETTHREADUICULTUREID fpGetThreadUICultureId // TODO: Don't rely on the LCID, only the name
57)
58{
59 WRAPPER_NO_CONTRACT;
60 CCompRC::SetDefaultCallbacks(
61 fpGetThreadUICultureNames,
62 fpGetThreadUICultureId
63 );
64
65}
66
67//*****************************************************************************
68// Public function to load a resource string
69//*****************************************************************************
70STDAPI UtilLoadStringRC(
71 UINT iResourceID,
72 __out_ecount(iMax) LPWSTR szBuffer,
73 int iMax,
74 int bQuiet
75)
76{
77 WRAPPER_NO_CONTRACT;
78 return UtilLoadResourceString(bQuiet? CCompRC::Optional : CCompRC::Required,iResourceID, szBuffer, iMax);
79}
80
81HRESULT UtilLoadResourceString(CCompRC::ResourceCategory eCategory, UINT iResourceID, __out_ecount (iMax) LPWSTR szBuffer, int iMax)
82{
83 CONTRACTL
84 {
85 DISABLED(NOTHROW);
86 GC_NOTRIGGER;
87 SO_TOLERANT;
88 }
89 CONTRACTL_END;
90
91 HRESULT retVal = E_OUTOFMEMORY;
92
93 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW);
94 SString::Startup();
95 EX_TRY
96 {
97 CCompRC *pResourceDLL = CCompRC::GetDefaultResourceDll();
98
99 if (pResourceDLL != NULL)
100 {
101 retVal = pResourceDLL->LoadString(eCategory, iResourceID, szBuffer, iMax);
102 }
103 }
104 EX_CATCH
105 {
106 // Catch any errors and return E_OUTOFMEMORY;
107 retVal = E_OUTOFMEMORY;
108 }
109 EX_END_CATCH(SwallowAllExceptions);
110
111 END_SO_INTOLERANT_CODE;
112
113 return retVal;
114}
115
116#ifdef FEATURE_USE_LCID
117STDAPI UtilLoadStringRCEx(
118 LCID lcid,
119 UINT iResourceID,
120 __out_ecount(iMax) LPWSTR szBuffer,
121 int iMax,
122 int bQuiet,
123 int *pcwchUsed
124)
125{
126 CONTRACTL
127 {
128 DISABLED(NOTHROW);
129 GC_NOTRIGGER;
130 SO_TOLERANT;
131 }
132 CONTRACTL_END;
133
134 HRESULT retVal = E_OUTOFMEMORY;
135
136 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW);
137 EX_TRY
138 {
139 SString::Startup();
140 CCompRC *pResourceDLL = CCompRC::GetDefaultResourceDll();
141
142 if (pResourceDLL != NULL)
143 {
144 retVal = pResourceDLL->LoadString(bQuiet? CCompRC::Optional : CCompRC::Required,lcid, iResourceID, szBuffer, iMax, pcwchUsed);
145 }
146 }
147 EX_CATCH
148 {
149 // Catch any errors and return E_OUTOFMEMORY;
150 retVal = E_OUTOFMEMORY;
151 }
152 EX_END_CATCH(SwallowAllExceptions);
153 END_SO_INTOLERANT_CODE;
154
155 return retVal;
156}
157#endif //FEATURE_USE_LCID
158
159//*****************************************************************************
160// Format a Runtime Error message.
161//*****************************************************************************
162HRESULT __cdecl FormatRuntimeErrorVa(
163 __inout_ecount(cchMsg) WCHAR *rcMsg, // Buffer into which to format.
164 ULONG cchMsg, // Size of buffer, characters.
165 HRESULT hrRpt, // The HR to report.
166 va_list marker) // Optional args.
167{
168 CONTRACTL
169 {
170 NOTHROW;
171 GC_NOTRIGGER;
172 }
173 CONTRACTL_END;
174
175 WCHAR rcBuf[512]; // Resource string.
176 HRESULT hr;
177
178 // Ensure nul termination.
179 *rcMsg = W('\0');
180
181 // If this is one of our errors or if it is simply a resource ID, then grab the error from the rc file.
182 if ((HRESULT_FACILITY(hrRpt) == FACILITY_URT) || (HIWORD(hrRpt) == 0))
183 {
184 hr = UtilLoadStringRC(LOWORD(hrRpt), rcBuf, NumItems(rcBuf), true);
185 if (hr == S_OK)
186 {
187 _vsnwprintf_s(rcMsg, cchMsg, _TRUNCATE, rcBuf, marker);
188 }
189 }
190 // Otherwise it isn't one of ours, so we need to see if the system can
191 // find the text for it.
192 else
193 {
194#ifdef FEATURE_USE_LCID
195 if (WszFormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
196 0, hrRpt, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
197 rcMsg, cchMsg, 0/*<TODO>@todo: marker</TODO>*/))
198#else
199 if (WszFormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
200 0, hrRpt, 0,
201 rcMsg, cchMsg, 0/*<TODO>@todo: marker</TODO>*/))
202#endif
203 {
204 hr = S_OK;
205
206 // System messages contain a trailing \r\n, which we don't want normally.
207 size_t iLen = wcslen(rcMsg);
208 if (iLen > 3 && rcMsg[iLen - 2] == '\r' && rcMsg[iLen - 1] == '\n')
209 rcMsg[iLen - 2] = '\0';
210 }
211 else
212 hr = HRESULT_FROM_GetLastError();
213 }
214
215 // If we failed to find the message anywhere, then issue a hard coded message.
216 if (FAILED(hr))
217 {
218 _snwprintf_s(rcMsg, cchMsg, _TRUNCATE, W("Common Language Runtime Internal error: 0x%08x"), hrRpt);
219 DEBUG_STMT(DbgWriteEx(rcMsg));
220 }
221
222 return hrRpt;
223} // FormatRuntimeErrorVa
224
225//*****************************************************************************
226// Format a Runtime Error message, varargs.
227//*****************************************************************************
228HRESULT __cdecl FormatRuntimeError(
229 __out_ecount(cchMsg) WCHAR *rcMsg, // Buffer into which to format.
230 ULONG cchMsg, // Size of buffer, characters.
231 HRESULT hrRpt, // The HR to report.
232 ...) // Optional args.
233{
234 WRAPPER_NO_CONTRACT;
235 va_list marker; // User text.
236 va_start(marker, hrRpt);
237 hrRpt = FormatRuntimeErrorVa(rcMsg, cchMsg, hrRpt, marker);
238 va_end(marker);
239 return hrRpt;
240}
241
242#ifdef FEATURE_COMINTEROP
243//*****************************************************************************
244// Create, fill out and set an error info object. Note that this does not fill
245// out the IID for the error object; that is done elsewhere.
246//*****************************************************************************
247HRESULT FillErrorInfo( // Return status.
248 LPCWSTR szMsg, // Error message.
249 DWORD dwHelpContext) // Help context.
250{
251 CONTRACTL
252 {
253 NOTHROW;
254 GC_NOTRIGGER;
255 }
256 CONTRACTL_END;
257
258 ICreateErrorInfo *pICreateErr = NULL; // Error info creation Iface pointer.
259 IErrorInfo *pIErrInfo = NULL; // The IErrorInfo interface.
260 HRESULT hr; // Return status.
261
262 // Get the ICreateErrorInfo pointer.
263 hr = S_OK;
264 EX_TRY
265 {
266 hr = CreateErrorInfo(&pICreateErr);
267 }
268 EX_CATCH
269 {
270 hr = GET_EXCEPTION()->GetHR();
271 }
272 EX_END_CATCH(SwallowAllExceptions);
273
274 if (FAILED(hr))
275 return (hr);
276
277 // Set message text description.
278 if (FAILED(hr = pICreateErr->SetDescription((LPWSTR) szMsg)))
279 goto Exit1;
280
281 // suppress PreFast warning about passing literal string to non-const API.
282 // This API (ICreateErrorInfo::SetHelpFile) is documented to take a const argument, but
283 // we can't put const in the signature because it would break existing implementors of
284 // the API.
285#ifdef _PREFAST_
286#pragma prefast(push)
287#pragma warning(disable:6298)
288#endif
289
290 // Set the help file and help context.
291 //<TODO>@todo: we don't have a help file yet.</TODO>
292 if (FAILED(hr = pICreateErr->SetHelpFile(const_cast<wchar_t*>(W("complib.hlp")))) ||
293 FAILED(hr = pICreateErr->SetHelpContext(dwHelpContext)))
294 goto Exit1;
295
296#ifdef _PREFAST_
297#pragma prefast(pop)
298#endif
299
300 // Get the IErrorInfo pointer.
301 if (FAILED(hr = pICreateErr->QueryInterface(IID_IErrorInfo, (PVOID *) &pIErrInfo)))
302 goto Exit1;
303
304 // Save the error and release our local pointers.
305 {
306 // If we get here, we have loaded oleaut32.dll.
307 CONTRACT_VIOLATION(ThrowsViolation);
308 SetErrorInfo(0L, pIErrInfo);
309 }
310
311Exit1:
312 pICreateErr->Release();
313 if (pIErrInfo) {
314 pIErrInfo->Release();
315 }
316 return hr;
317}
318#endif // FEATURE_COMINTEROP
319
320//*****************************************************************************
321// This function will post an error for the client. If the LOWORD(hrRpt) can
322// be found as a valid error message, then it is loaded and formatted with
323// the arguments passed in. If it cannot be found, then the error is checked
324// against FormatMessage to see if it is a system error. System errors are
325// not formatted so no add'l parameters are required. If any errors in this
326// process occur, hrRpt is returned for the client with no error posted.
327//*****************************************************************************
328extern "C"
329HRESULT __cdecl PostErrorVA( // Returned error.
330 HRESULT hrRpt, // Reported error.
331 va_list marker) // Error arguments.
332{
333 CONTRACTL
334 {
335 NOTHROW;
336 GC_NOTRIGGER;
337 ENTRY_POINT;
338 }
339 CONTRACTL_END;
340
341#ifdef FEATURE_COMINTEROP
342
343 const DWORD cchMsg = 4096;
344 WCHAR *rcMsg = (WCHAR*)alloca(cchMsg * sizeof(WCHAR)); // Error message.
345 HRESULT hr;
346
347 BEGIN_ENTRYPOINT_NOTHROW;
348
349 // Return warnings without text.
350 if (!FAILED(hrRpt))
351 goto ErrExit;
352
353 // If we are already out of memory or out of stack or the thread is in some bad state,
354 // we don't want throw gasoline on the fire by calling ErrorInfo stuff below (which can
355 // trigger a delayload of oleaut32.dll). We don't need to embellish transient errors
356 // so just return this without text.
357 if (Exception::IsTransient(hrRpt))
358 {
359 goto ErrExit;
360 }
361
362 // Format the error.
363 FormatRuntimeErrorVa(rcMsg, cchMsg, hrRpt, marker);
364
365 // Turn the error into a posted error message. If this fails, we still
366 // return the original error message since a message caused by our error
367 // handling system isn't going to give you a clue about the original error.
368 hr = FillErrorInfo(rcMsg, LOWORD(hrRpt));
369 _ASSERTE(hr == S_OK);
370
371ErrExit:
372
373 END_ENTRYPOINT_NOTHROW;
374
375#endif // FEATURE_COMINTEROP
376
377 return (hrRpt);
378} // PostErrorVA
379
380#endif //!FEATURE_UTILCODE_NO_DEPENDENCIES
381
382//*****************************************************************************
383// This function will post an error for the client. If the LOWORD(hrRpt) can
384// be found as a valid error message, then it is loaded and formatted with
385// the arguments passed in. If it cannot be found, then the error is checked
386// against FormatMessage to see if it is a system error. System errors are
387// not formatted so no add'l parameters are required. If any errors in this
388// process occur, hrRpt is returned for the client with no error posted.
389//*****************************************************************************
390extern "C"
391HRESULT __cdecl PostError(
392 HRESULT hrRpt, // Reported error.
393 ...) // Error arguments.
394{
395#ifndef FEATURE_UTILCODE_NO_DEPENDENCIES
396 WRAPPER_NO_CONTRACT;
397 va_list marker; // User text.
398 va_start(marker, hrRpt);
399 hrRpt = PostErrorVA(hrRpt, marker);
400 va_end(marker);
401#endif //!FEATURE_UTILCODE_NO_DEPENDENCIES
402 return hrRpt;
403}
404