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. |
29 | HRESULT 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 |
35 | void ShutdownCompRC() |
36 | { |
37 | CCompRC::ShutdownDefaultResourceDll(); |
38 | } |
39 | #endif /* SHOULD_WE_CLEANUP */ |
40 | |
41 | void 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 | //***************************************************************************** |
54 | void 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 | //***************************************************************************** |
70 | STDAPI 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 | |
81 | HRESULT 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 |
117 | STDAPI 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 | //***************************************************************************** |
162 | HRESULT __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 | //***************************************************************************** |
228 | HRESULT __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 | //***************************************************************************** |
247 | HRESULT 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 | |
311 | Exit1: |
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 | //***************************************************************************** |
328 | extern "C" |
329 | HRESULT __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 | |
371 | ErrExit: |
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 | //***************************************************************************** |
390 | extern "C" |
391 | HRESULT __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 | |