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