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
6//
7// ---------------------------------------------------------------------------
8// CLREx.h
9// ---------------------------------------------------------------------------
10
11
12#ifndef _CLREX_H_
13#define _CLREX_H_
14
15#include <ex.h>
16
17#include "runtimeexceptionkind.h"
18#include "interoputil.h"
19
20class BaseBind;
21class AssemblySpec;
22class PEFile;
23class PEAssembly;
24
25struct StackTraceElement
26{
27 UINT_PTR ip;
28 UINT_PTR sp;
29 PTR_MethodDesc pFunc;
30 // TRUE if this element represents the last frame of the foreign
31 // exception stack trace.
32 BOOL fIsLastFrameFromForeignStackTrace;
33
34 bool operator==(StackTraceElement const & rhs) const
35 {
36 return ip == rhs.ip
37 && sp == rhs.sp
38 && pFunc == rhs.pFunc;
39 }
40
41 bool operator!=(StackTraceElement const & rhs) const
42 {
43 return !(*this == rhs);
44 }
45};
46
47class StackTraceInfo
48{
49private:
50 // for building stack trace info
51 StackTraceElement* m_pStackTrace; // pointer to stack trace storage
52 unsigned m_cStackTrace; // size of stack trace storage
53 unsigned m_dFrameCount; // current frame in stack trace
54 unsigned m_cDynamicMethodItems; // number of items in the Dynamic Method array
55 unsigned m_dCurrentDynamicIndex; // index of the next location where the resolver object will be stored
56
57public:
58 void Init();
59 BOOL IsEmpty();
60 void AllocateStackTrace();
61 void ClearStackTrace();
62 void FreeStackTrace();
63 void SaveStackTrace(BOOL bAllowAllocMem, OBJECTHANDLE hThrowable, BOOL bReplaceStack, BOOL bSkipLastElement);
64 BOOL AppendElement(BOOL bAllowAllocMem, UINT_PTR currentIP, UINT_PTR currentSP, MethodDesc* pFunc, CrawlFrame* pCf);
65
66 void GetLeafFrameInfo(StackTraceElement* pStackTraceElement);
67};
68
69
70// ---------------------------------------------------------------------------
71// CLRException represents an exception which has a managed representation.
72// It adds the generic method GetThrowable().
73// ---------------------------------------------------------------------------
74class CLRException : public Exception
75{
76 friend bool DebugIsEECxxExceptionPointer(void* pv);
77 friend class CLRLastThrownObjectException;
78 private:
79 static const int c_type = 0x434c5220; // 'CLR '
80
81 protected:
82 OBJECTHANDLE m_throwableHandle;
83
84 void SetThrowableHandle(OBJECTHANDLE throwable);
85 OBJECTHANDLE GetThrowableHandle() { return m_throwableHandle; }
86
87
88 CLRException();
89public:
90 ~CLRException();
91
92 OBJECTREF GetThrowable();
93
94 // Dynamic type query for catchers
95 static int GetType() {LIMITED_METHOD_CONTRACT; return c_type; }
96 virtual int GetInstanceType() { LIMITED_METHOD_CONTRACT; return c_type; }
97 BOOL IsType(int type) { WRAPPER_NO_CONTRACT; return type == c_type || Exception::IsType(type); }
98
99 BOOL IsSameInstanceType(Exception *pException)
100 {
101 STATIC_CONTRACT_MODE_COOPERATIVE;
102 STATIC_CONTRACT_GC_TRIGGERS;
103 STATIC_CONTRACT_NOTHROW;
104
105 if (pException->GetInstanceType() != GetInstanceType())
106 {
107 return FALSE;
108 }
109 OBJECTREF mine = GetThrowable();
110 OBJECTREF other = ((CLRException*)pException)->GetThrowable();
111 return mine != NULL && other != NULL &&
112 mine->GetMethodTable() == other->GetMethodTable();
113 }
114
115 // Overrides
116 virtual BOOL IsDomainBound()
117 {
118 //@TODO special case for preallocated exceptions?
119 return TRUE;
120 }
121
122 HRESULT GetHR();
123 IErrorInfo *GetErrorInfo();
124 HRESULT SetErrorInfo();
125
126 void GetMessage(SString &result);
127
128 protected:
129
130 virtual OBJECTREF CreateThrowable() { LIMITED_METHOD_CONTRACT; return NULL; }
131
132 public: // These are really private, but are used by the exception macros
133
134
135 // Accessors for all the preallocated exception objects.
136 static OBJECTREF GetPreallocatedBaseException();
137 static OBJECTREF GetPreallocatedOutOfMemoryException();
138 static OBJECTREF GetPreallocatedRudeThreadAbortException();
139 static OBJECTREF GetPreallocatedThreadAbortException();
140 static OBJECTREF GetPreallocatedStackOverflowException();
141 static OBJECTREF GetPreallocatedExecutionEngineException();
142
143 // Accessors for all the preallocated exception handles.
144 static OBJECTHANDLE GetPreallocatedOutOfMemoryExceptionHandle();
145 static OBJECTHANDLE GetPreallocatedRudeThreadAbortExceptionHandle();
146 static OBJECTHANDLE GetPreallocatedThreadAbortExceptionHandle();
147 static OBJECTHANDLE GetPreallocatedStackOverflowExceptionHandle();
148 static OBJECTHANDLE GetPreallocatedExecutionEngineExceptionHandle();
149 static OBJECTHANDLE GetPreallocatedHandleForObject(OBJECTREF o);
150
151 // Use these to determine if a handle or object ref is one of the preallocated handles or object refs.
152 static BOOL IsPreallocatedExceptionObject(OBJECTREF o);
153 static BOOL IsPreallocatedExceptionHandle(OBJECTHANDLE h);
154
155 // Prefer a new OOM exception if we can make one. If we cannot, then give back the pre-allocated
156 // one.
157 static OBJECTREF GetBestOutOfMemoryException();
158
159 static OBJECTREF GetThrowableFromException(Exception *pException);
160 static OBJECTREF GetThrowableFromExceptionRecord(EXCEPTION_RECORD *pExceptionRecord);
161
162 class HandlerState : public Exception::HandlerState
163 {
164 public:
165 Thread* m_pThread;
166 Frame* m_pFrame;
167 BOOL m_fPreemptiveGCDisabled;
168
169 enum NonNullThread
170 {
171 ThreadIsNotNull
172 };
173
174 HandlerState(Thread * pThread);
175 HandlerState(Thread * pThread, NonNullThread dummy);
176
177 void CleanupTry();
178 void SetupCatch(INDEBUG_COMMA(__in_z const char * szFile) int lineNum);
179#ifdef LOGGING // Use parent implementation that inlines into nothing in retail build
180 void SucceedCatch();
181#endif
182 void SetupFinally();
183 };
184};
185
186// prototype for helper function to get exception object from thread's LastThrownObject.
187void GetLastThrownObjectExceptionFromThread_Internal(Exception **ppException);
188
189
190// ---------------------------------------------------------------------------
191// EEException is a CLR exception subclass which has purely unmanaged representation.
192// The standard methods will not do any GC dangerous operations. Thus you
193// can throw and catch such an exception without regard to GC mode.
194// ---------------------------------------------------------------------------
195
196class EEException : public CLRException
197{
198 friend bool DebugIsEECxxExceptionPointer(void* pv);
199
200 private:
201 static const int c_type = 0x45452020; // 'EE '
202
203 public:
204 const RuntimeExceptionKind m_kind;
205
206 EEException(RuntimeExceptionKind kind);
207 EEException(HRESULT hr);
208
209 // Dynamic type query for catchers
210 static int GetType() {LIMITED_METHOD_CONTRACT; return c_type; }
211 virtual int GetInstanceType() { LIMITED_METHOD_CONTRACT; return c_type; }
212 BOOL IsType(int type) { WRAPPER_NO_CONTRACT; return type == c_type || CLRException::IsType(type); }
213
214 BOOL IsSameInstanceType(Exception *pException)
215 {
216 WRAPPER_NO_CONTRACT;
217 return pException->GetInstanceType() == GetType() && ((EEException*)pException)->m_kind == m_kind;
218 }
219
220 // Virtual overrides
221 HRESULT GetHR();
222 IErrorInfo *GetErrorInfo();
223 void GetMessage(SString &result);
224 OBJECTREF CreateThrowable();
225
226 // GetThrowableMessage returns a message to be stored in the throwable.
227 // Returns FALSE if there is no useful value.
228 virtual BOOL GetThrowableMessage(SString &result);
229
230 static BOOL GetResourceMessage(UINT iResourceID, SString &result,
231 const SString &arg1 = SString::Empty(), const SString &arg2 = SString::Empty(),
232 const SString &arg3 = SString::Empty(), const SString &arg4 = SString::Empty(),
233 const SString &arg5 = SString::Empty(), const SString &arg6 = SString::Empty());
234
235 // Note: reKind-->hr is a one-to-many relationship.
236 //
237 // each reKind is associated with one or more hresults.
238 // every hresult is associated with exactly one reKind (with kCOMException being the catch-all.)
239 static RuntimeExceptionKind GetKindFromHR(HRESULT hr, bool fIsWinRtMode = false);
240 protected:
241 static HRESULT GetHRFromKind(RuntimeExceptionKind reKind);
242
243#ifdef _DEBUG
244 EEException() : m_kind(kException)
245 {
246 // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer.
247 // We need a variant which does not allocate memory.
248 }
249#endif // _DEBUG
250
251 virtual Exception *CloneHelper()
252 {
253 WRAPPER_NO_CONTRACT;
254 return new EEException(m_kind);
255 }
256
257};
258
259// ---------------------------------------------------------------------------
260// EEMessageException is an EE exception subclass composed of a type and
261// an unmanaged message of some sort.
262// ---------------------------------------------------------------------------
263
264class EEMessageException : public EEException
265{
266 friend bool DebugIsEECxxExceptionPointer(void* pv);
267
268 private:
269 HRESULT m_hr;
270 UINT m_resID;
271 InlineSString<32> m_arg1;
272 InlineSString<32> m_arg2;
273 SString m_arg3;
274 SString m_arg4;
275 SString m_arg5;
276 SString m_arg6;
277
278 public:
279 EEMessageException(RuntimeExceptionKind kind, UINT resID = 0, LPCWSTR szArg1 = NULL, LPCWSTR szArg2 = NULL,
280 LPCWSTR szArg3 = NULL, LPCWSTR szArg4 = NULL, LPCWSTR szArg5 = NULL, LPCWSTR szArg6 = NULL);
281
282 EEMessageException(HRESULT hr);
283
284 EEMessageException(HRESULT hr, bool fUseCOMException);
285
286 EEMessageException(HRESULT hr, UINT resID, LPCWSTR szArg1 = NULL, LPCWSTR szArg2 = NULL, LPCWSTR szArg3 = NULL,
287 LPCWSTR szArg4 = NULL, LPCWSTR szArg5 = NULL, LPCWSTR szArg6 = NULL);
288
289 EEMessageException(RuntimeExceptionKind kind, HRESULT hr, UINT resID, LPCWSTR szArg1 = NULL, LPCWSTR szArg2 = NULL,
290 LPCWSTR szArg3 = NULL, LPCWSTR szArg4 = NULL, LPCWSTR szArg5 = NULL, LPCWSTR szArg6 = NULL);
291
292 // Virtual overrides
293 HRESULT GetHR();
294
295 BOOL GetThrowableMessage(SString &result);
296
297 UINT GetResID(void) { LIMITED_METHOD_CONTRACT; return m_resID; }
298
299 static BOOL IsEEMessageException(Exception *pException)
300 {
301 return (*(PVOID*)pException == GetEEMessageExceptionVPtr());
302 }
303
304 protected:
305
306 virtual Exception *CloneHelper()
307 {
308 WRAPPER_NO_CONTRACT;
309 return new EEMessageException(
310 m_kind, m_hr, m_resID, m_arg1, m_arg2, m_arg3, m_arg4, m_arg5, m_arg6);
311 }
312
313
314 private:
315
316 static PVOID GetEEMessageExceptionVPtr()
317 {
318 CONTRACT (PVOID)
319 {
320 WRAPPER(THROWS);
321 WRAPPER(GC_TRIGGERS);
322 MODE_ANY;
323 POSTCONDITION(CheckPointer(RETVAL));
324 }
325 CONTRACT_END;
326
327 EEMessageException boilerplate(E_FAIL);
328 RETURN (PVOID&)boilerplate;
329 }
330
331 BOOL GetResourceMessage(UINT iResourceID, SString &result);
332
333#ifdef _DEBUG
334 EEMessageException()
335 {
336 // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer.
337 // We need a variant which does not allocate memory.
338 }
339#endif // _DEBUG
340};
341
342// ---------------------------------------------------------------------------
343// EEResourceException is an EE exception subclass composed of a type and
344// an message using a managed exception resource.
345// ---------------------------------------------------------------------------
346
347class EEResourceException : public EEException
348{
349 friend bool DebugIsEECxxExceptionPointer(void* pv);
350
351 private:
352 InlineSString<32> m_resourceName;
353
354 public:
355 EEResourceException(RuntimeExceptionKind kind, const SString &resourceName);
356
357 // Unmanaged message text containing only the resource name (GC safe)
358 void GetMessage(SString &result);
359
360 // Throwable message containig the resource contents (not GC safe)
361 BOOL GetThrowableMessage(SString &result);
362
363 protected:
364
365 virtual Exception *CloneHelper()
366 {
367 WRAPPER_NO_CONTRACT;
368 return new EEResourceException(m_kind, m_resourceName);
369 }
370
371private:
372#ifdef _DEBUG
373 EEResourceException()
374 {
375 // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer.
376 // We need a variant which does not allocate memory.
377 }
378#endif // _DEBUG
379};
380
381// ---------------------------------------------------------------------------
382// EECOMException is an EE exception subclass composed of COM-generated data.
383// Note that you must ensure that the COM data was not derived from a wrapper
384// on a managed Exception object. (If so, you must compose the exception from
385// the managed object itself.)
386// ---------------------------------------------------------------------------
387
388struct ExceptionData
389{
390 HRESULT hr;
391 BSTR bstrDescription;
392 BSTR bstrSource;
393 BSTR bstrHelpFile;
394 DWORD dwHelpContext;
395 GUID guid;
396#ifdef FEATURE_COMINTEROP
397 BSTR bstrRestrictedError; // Returned from IRestrictedErrorInfo::GetErrorDetails
398 BSTR bstrReference; // Returned from IRestrictedErrorInfo::GetReference
399 BSTR bstrCapabilitySid; // Returned from IRestrictedErrorInfo::GetErrorDetails
400 IUnknown *pRestrictedErrorInfo; // AddRef-ed RestrictedErrorInfo pointer
401 // We need to keep this alive as long as user need the reference
402 BOOL bHasLanguageRestrictedErrorInfo;
403#endif // FEATURE_COMINTEROP
404};
405
406class EECOMException : public EEException
407{
408 friend bool DebugIsEECxxExceptionPointer(void* pv);
409
410 private:
411 ExceptionData m_ED;
412
413 public:
414
415 EECOMException(EXCEPINFO *pExcepInfo);
416 EECOMException(ExceptionData *pED);
417 EECOMException(
418 HRESULT hr,
419 IErrorInfo *pErrInfo,
420 bool fUseCOMException,
421 IRestrictedErrorInfo *pRestrictedErrInfo,
422 BOOL bHasLanguageRestrictedErrorInfo
423 COMMA_INDEBUG(BOOL bCheckInProcCCWTearOff = TRUE));
424 ~EECOMException();
425
426 // Virtual overrides
427 HRESULT GetHR();
428
429 BOOL GetThrowableMessage(SString &result);
430 OBJECTREF CreateThrowable();
431
432 protected:
433
434 virtual Exception *CloneHelper()
435 {
436 WRAPPER_NO_CONTRACT;
437 return new EECOMException(&m_ED);
438 }
439
440private:
441#ifdef _DEBUG
442 EECOMException()
443 {
444 // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer.
445 // We need a variant which does not allocate memory.
446 ZeroMemory(&m_ED, sizeof(m_ED));
447 }
448#endif // _DEBUG
449};
450
451// ---------------------------------------------------------------------------
452// EEFieldException is an EE exception subclass composed of a field
453// ---------------------------------------------------------------------------
454class EEFieldException : public EEException
455{
456 friend bool DebugIsEECxxExceptionPointer(void* pv);
457
458 private:
459 FieldDesc *m_pFD;
460 MethodDesc *m_pAccessingMD;
461 SString m_additionalContext;
462 UINT m_messageID;
463
464 public:
465 EEFieldException(FieldDesc *pField);
466 EEFieldException(FieldDesc *pField, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID);
467
468 BOOL GetThrowableMessage(SString &result);
469 virtual BOOL IsDomainBound() {return TRUE;};
470protected:
471
472 virtual Exception *CloneHelper()
473 {
474 WRAPPER_NO_CONTRACT;
475 return new EEFieldException(m_pFD, m_pAccessingMD, m_additionalContext, m_messageID);
476 }
477
478private:
479#ifdef _DEBUG
480 EEFieldException()
481 {
482 // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer.
483 // We need a variant which does not allocate memory.
484 }
485#endif // _DEBUG
486};
487
488// ---------------------------------------------------------------------------
489// EEMethodException is an EE exception subclass composed of a field
490// ---------------------------------------------------------------------------
491
492class EEMethodException : public EEException
493{
494 friend bool DebugIsEECxxExceptionPointer(void* pv);
495
496 private:
497 MethodDesc *m_pMD;
498 MethodDesc *m_pAccessingMD;
499 SString m_additionalContext;
500 UINT m_messageID;
501
502 public:
503 EEMethodException(MethodDesc *pMethod);
504 EEMethodException(MethodDesc *pMethod, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID);
505
506 BOOL GetThrowableMessage(SString &result);
507 virtual BOOL IsDomainBound() {return TRUE;};
508 protected:
509
510 virtual Exception *CloneHelper()
511 {
512 WRAPPER_NO_CONTRACT;
513 return new EEMethodException(m_pMD, m_pAccessingMD, m_additionalContext, m_messageID);
514 }
515
516private:
517#ifdef _DEBUG
518 EEMethodException()
519 {
520 // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer.
521 // We need a variant which does not allocate memory.
522 }
523#endif // _DEBUG
524};
525
526// ---------------------------------------------------------------------------
527// EETypeAccessException is an EE exception subclass composed of a type being
528// illegally accessed and the method doing the access
529// ---------------------------------------------------------------------------
530
531class EETypeAccessException : public EEException
532{
533 friend bool DebugIsEECxxExceptionPointer(void* pv);
534
535 private:
536 MethodTable *m_pMT;
537 MethodDesc *m_pAccessingMD;
538 SString m_additionalContext;
539 UINT m_messageID;
540
541 public:
542 EETypeAccessException(MethodTable *pMT);
543 EETypeAccessException(MethodTable *pMT, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID);
544
545 BOOL GetThrowableMessage(SString &result);
546 virtual BOOL IsDomainBound() {return TRUE;};
547 protected:
548
549 virtual Exception *CloneHelper()
550 {
551 WRAPPER_NO_CONTRACT;
552 return new EETypeAccessException(m_pMT, m_pAccessingMD, m_additionalContext, m_messageID);
553 }
554
555private:
556#ifdef _DEBUG
557 EETypeAccessException()
558 {
559 // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer.
560 // We need a variant which does not allocate memory.
561 }
562#endif // _DEBUG
563};
564
565// ---------------------------------------------------------------------------
566// EEArgumentException is an EE exception subclass representing a bad argument
567// exception
568// ---------------------------------------------------------------------------
569
570class EEArgumentException : public EEException
571{
572 friend bool DebugIsEECxxExceptionPointer(void* pv);
573
574 private:
575 InlineSString<32> m_argumentName;
576 InlineSString<32> m_resourceName;
577
578 public:
579 EEArgumentException(RuntimeExceptionKind reKind, LPCWSTR pArgName,
580 LPCWSTR wszResourceName);
581
582 // @todo: GetMessage
583
584 OBJECTREF CreateThrowable();
585
586 protected:
587
588 virtual Exception *CloneHelper()
589 {
590 WRAPPER_NO_CONTRACT;
591 return new EEArgumentException(m_kind, m_argumentName, m_resourceName);
592 }
593
594private:
595#ifdef _DEBUG
596 EEArgumentException()
597 {
598 // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer.
599 // We need a variant which does not allocate memory.
600 }
601#endif // _DEBUG
602};
603
604// ---------------------------------------------------------------------------
605// EETypeLoadException is an EE exception subclass representing a type loading
606// error
607// ---------------------------------------------------------------------------
608
609class EETypeLoadException : public EEException
610{
611 friend bool DebugIsEECxxExceptionPointer(void* pv);
612
613 private:
614 InlineSString<64> m_fullName;
615 SString m_pAssemblyName;
616 SString m_pMessageArg;
617 UINT m_resIDWhy;
618
619 public:
620 EETypeLoadException(LPCUTF8 pszNameSpace, LPCUTF8 pTypeName,
621 LPCWSTR pAssemblyName, LPCUTF8 pMessageArg, UINT resIDWhy);
622
623 EETypeLoadException(LPCWSTR pFullTypeName,
624 LPCWSTR pAssemblyName, LPCUTF8 pMessageArg, UINT resIDWhy);
625
626 // virtual overrides
627 void GetMessage(SString &result);
628 OBJECTREF CreateThrowable();
629
630 protected:
631
632 virtual Exception *CloneHelper()
633 {
634 WRAPPER_NO_CONTRACT;
635 return new EETypeLoadException(m_fullName, m_pAssemblyName, m_pMessageArg, m_resIDWhy);
636 }
637
638 private:
639 EETypeLoadException(const InlineSString<64> &fullName, LPCWSTR pAssemblyName,
640 const SString &pMessageArg, UINT resIDWhy)
641 : EEException(kTypeLoadException),
642 m_fullName(fullName),
643 m_pAssemblyName(pAssemblyName),
644 m_pMessageArg(pMessageArg),
645 m_resIDWhy(resIDWhy)
646 {
647 WRAPPER_NO_CONTRACT;
648 }
649
650
651#ifdef _DEBUG
652 EETypeLoadException()
653 {
654 // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer.
655 // We need a variant which does not allocate memory.
656 }
657#endif // _DEBUG
658};
659
660// ---------------------------------------------------------------------------
661// EEFileLoadException is an EE exception subclass representing a file loading
662// error
663// ---------------------------------------------------------------------------
664
665class EEFileLoadException : public EEException
666{
667 friend bool DebugIsEECxxExceptionPointer(void* pv);
668
669 private:
670 SString m_name;
671 void *m_pFusionLog;
672 HRESULT m_hr;
673
674
675 public:
676
677 EEFileLoadException(const SString &name, HRESULT hr, void *pFusionLog = NULL, Exception *pInnerException = NULL);
678 ~EEFileLoadException();
679
680 // virtual overrides
681 HRESULT GetHR()
682 {
683 LIMITED_METHOD_DAC_CONTRACT;
684 return m_hr;
685 }
686 void GetMessage(SString &result);
687 void GetName(SString &result);
688 OBJECTREF CreateThrowable();
689
690 static RuntimeExceptionKind GetFileLoadKind(HRESULT hr);
691 static void DECLSPEC_NORETURN Throw(AssemblySpec *pSpec, HRESULT hr, Exception *pInnerException = NULL);
692 static void DECLSPEC_NORETURN Throw(PEFile *pFile, HRESULT hr, Exception *pInnerException = NULL);
693 static void DECLSPEC_NORETURN Throw(LPCWSTR path, HRESULT hr, Exception *pInnerException = NULL);
694 static void DECLSPEC_NORETURN Throw(PEAssembly *parent, const void *memory, COUNT_T size, HRESULT hr, Exception *pInnerException = NULL);
695 static BOOL CheckType(Exception* ex); // typeof(EEFileLoadException)
696
697 protected:
698 virtual Exception *CloneHelper()
699 {
700 WRAPPER_NO_CONTRACT;
701 return new EEFileLoadException(m_name, m_hr, m_pFusionLog);
702 }
703
704 private:
705#ifdef _DEBUG
706 EEFileLoadException() : m_pFusionLog(NULL)
707 {
708 // Used only for DebugIsEECxxExceptionPointer to get the vtable pointer.
709 // We need a variant which does not allocate memory.
710 }
711#endif // _DEBUG
712
713 void SetFileName(const SString &fileName, BOOL removePath);
714};
715
716// -------------------------------------------------------------------------------------------------------
717// Throw/catch macros. These are derived from the generic EXCEPTION macros,
718// but add extra functionality for cleaning up thread state on catches
719//
720// Usage:
721// EX_TRY
722// {
723// EX_THROW(EEMessageException, (kind, L"Failure message"));
724// }
725// EX_CATCH
726// {
727// EX_RETHROW()
728// }
729// EX_END_CATCH(RethrowTerminalExceptions or RethrowCorruptingExceptions)
730// --------------------------------------------------------------------------------------------------------
731
732// In DAC builds, we don't want to override the normal utilcode exception handling.
733// We're not actually running in the CLR, but we may need access to some CLR-exception
734// related data structures elsewhere in this header file in order to analyze CLR
735// exceptions that occurred in the target.
736#if !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE)
737
738#define GET_THROWABLE() CLRException::GetThrowableFromException(GET_EXCEPTION())
739
740#ifdef FEATURE_CORRUPTING_EXCEPTIONS
741
742// For the VM folder, we redefine SET_CE_RETHROW_FLAG_FOR_EX_CATCH to also check the
743// corruption severity when deciding whether to rethrow them or not.
744//
745// We also check the global override flag incase it has been set to force pre-V4 behaviour.
746//
747// Doing the checks for "__fCaughtSO" and "__fCaughtNonCxx" will ensure that we check for
748// corruption severity only if the last exception was a managed exception that could have been rethrown in the VM.
749// When "(__fCaughtSO == FALSE) && (__fCaughtNonCxx == true)" is true, it implies we are dealing with a managed exception
750// inside the VM that is represented by the CLRLastThrownObjectException instance (see EX_TRY/EX_CATCH implementation in VM
751// folder to see how CLRLastThrownObjectException is used).
752//
753// This macro also supports the following scenarios:
754//
755// Scenario 1
756// ----------
757//
758// [VM1] -> [VM2] -> <managed code>
759//
760// If a managed exception is swallowed by an EX_CATCH in native function VM2, which then returns back
761// to native function VM1 that throws, for example, a VM C++ exception, an EX_CATCH(RethrowCorruptingExceptions)
762// in VM1 that catches the C++ exception will not rethrow since the last exception was not a managed CSE but
763// a C++ exception.
764//
765// A variation of this is for VM2 to return back in VM1, which calls VM3 that throws a VM C++ exception that
766// reaches VM1's EX_CATCH(RethrowCorruptingExceptions). VM1 shouldn't be rethrowing the exception in such a case.
767//
768// Scenario 2
769// ----------
770//
771// [VM1 - RethrowCSE] -> [VM2 - RethrowCSE] -> [VM3 - RethrowCSE] -> <managed code>
772//
773// When managed code throws a CSE (e.g. TargetInvocationException flagged as CSE), [VM3] will rethrow it and we will
774// enter EX_CATCH in VM2 which is supposed to rethrow it as well. But if the implementation of EX_CATCH in VM2 throws
775// another VM C++ exception (e.g. EEFileLoadException) *before* rethrow policy is applied, control will reach EX_CATCH
776// in VM1 that *shouldn't* rethrow (even though it has RethrowCSE as the policy) since the last exception was a VM C++
777// exception.
778//
779// Scenario 3
780// ----------
781//
782// This is about VM throwing a managed exception that gets handled either within the VM, with or without CLR's managed code
783// exception handler coming into the picture.
784//
785// This is explained in detail (alongwith relevant changes) in the implementation of RaiseTheException (in excep.cpp).
786
787#undef SET_CE_RETHROW_FLAG_FOR_EX_CATCH
788#define SET_CE_RETHROW_FLAG_FOR_EX_CATCH(expr) ((expr == TRUE) && \
789 (g_pConfig->LegacyCorruptedStateExceptionsPolicy() == false) && \
790 (CEHelper::IsProcessCorruptedStateException(GetCurrentExceptionCode(), FALSE) || \
791 ((!__state.DidCatchSO()) && (!__state.DidCatchCxx()) && \
792 CEHelper::IsLastActiveExceptionCorrupting(TRUE))))
793
794#endif // FEATURE_CORRUPTING_EXCEPTIONS
795
796#undef EX_TRY
797#define EX_TRY \
798 EX_TRY_CUSTOM(CLRException::HandlerState, (::GetThreadNULLOk()), CLRLastThrownObjectException)
799
800#undef EX_TRY_CPP_ONLY
801#define EX_TRY_CPP_ONLY \
802 EX_TRY_CUSTOM_CPP_ONLY(CLRException::HandlerState, (::GetThreadNULLOk()), CLRLastThrownObjectException)
803
804// Faster version with thread, skipping GetThread call
805#define EX_TRY_THREAD(pThread) \
806 EX_TRY_CUSTOM(CLRException::HandlerState, (pThread, CLRException::HandlerState::ThreadIsNotNull), CLRLastThrownObjectException)
807
808#if defined(_DEBUG)
809 // Redefine GET_EXCEPTION to validate CLRLastThrownObjectException as much as possible.
810 #undef GET_EXCEPTION
811 #define GET_EXCEPTION() (__pException == NULL ? __defaultException.Validate() : __pException.GetValue())
812#endif // _DEBUG
813
814// When we throw an exception, we need stay in SO-intolerant state and
815// probe for sufficient stack so that we don't SO during the processing.
816#undef HANDLE_SO_TOLERANCE_FOR_THROW
817#define HANDLE_SO_TOLERANCE_FOR_THROW STACK_PROBE_FOR_THROW(GetThread());
818
819LONG CLRNoCatchHandler(EXCEPTION_POINTERS* pExceptionInfo, PVOID pv);
820
821// Re-define the macro to add automatic restoration of the guard page to PAL_EXCEPT and PAL_EXCEPT_FILTER and
822// friends. Note: RestoreGuardPage will only do work if the guard page is not present.
823#undef PAL_SEH_RESTORE_GUARD_PAGE
824#define PAL_SEH_RESTORE_GUARD_PAGE \
825 if (__exCode == STATUS_STACK_OVERFLOW) \
826 { \
827 Thread *__pThread = GetThread(); \
828 if (__pThread != NULL) \
829 { \
830 __pThread->RestoreGuardPage(); \
831 } \
832 }
833
834#undef EX_TRY_NOCATCH
835#define EX_TRY_NOCATCH(ParamType, paramDef, paramRef) \
836 PAL_TRY(ParamType, __EXparam, paramRef) \
837 { \
838 CLRException::HandlerState __state(::GetThreadNULLOk()); \
839 PAL_TRY(ParamType, paramDef, __EXparam) \
840 {
841
842#undef EX_END_NOCATCH
843#define EX_END_NOCATCH \
844 ; \
845 } \
846 PAL_FINALLY \
847 { \
848 __state.CleanupTry(); \
849 } \
850 PAL_ENDTRY \
851 } \
852 PAL_EXCEPT_FILTER(CLRNoCatchHandler) \
853 { \
854 } \
855 PAL_ENDTRY
856
857//
858// We need a way to identify an exception in managed code that is rethrown from a new exception in managed code
859// when we get into our managed EH logic. Currently, we do that by checking the GC mode. If a thread has preemptive
860// GC enabled, but the IP of the exception is in mangaed code, then it must be a rethrow from unmanaged code
861// (including CLR code.) Therefore, we toggle the GC mode before the rethrow to indicate that. Note: we don't do
862// this if we've caught one of our internal C++ Exception objects: by definition, those don't come from managed
863// code, and this allows us to continue to use EX_RETHROW in no-trigger regions.
864//
865#undef EX_RETHROW
866#define EX_RETHROW \
867 do \
868 { \
869 /* don't embed file names in retail to save space and avoid IP */ \
870 /* a findstr /n will allow you to locate it in a pinch */ \
871 STRESS_LOG1(LF_EH, LL_INFO100, \
872 "EX_RETHROW " INDEBUG(__FILE__) " line %d\n", __LINE__); \
873 __pException.SuppressRelease(); \
874 if ((!__state.DidCatchCxx()) && (GetThread() != NULL)) \
875 { \
876 if (GetThread()->PreemptiveGCDisabled()) \
877 { \
878 LOG((LF_EH, LL_INFO10, "EX_RETHROW: going preemptive\n")); \
879 GetThread()->EnablePreemptiveGC(); \
880 } \
881 } \
882 PAL_CPP_RETHROW; \
883 } while (0)
884
885//
886// Note: we only restore the guard page if we did _not_ catch a C++ exception, since a SO exception is a SEH
887// exception.
888//
889// We also need to restore the SO tolerance state, including restoring the cookie for the current stack guard.
890//
891// For VM code EX_CATCH calls CLREXception::HandleState::SetupCatch().
892// When Stack guards are disabled we will tear down the process in
893// CLREXception::HandleState::SetupCatch() if there is a StackOverflow.
894// So we should not reach EX_ENDTRY when there is StackOverflow.
895// This change cannot be done in ex.h as for all other code
896// CLREXception::HandleState::SetupCatch() is not called rather
897// EXception::HandleState::SetupCatch() is called which is a nop.
898//
899#undef EX_ENDTRY
900#define EX_ENDTRY \
901 PAL_CPP_ENDTRY \
902 SO_INFRASTRUCTURE_CODE(if (__state.DidCatch()) { RESTORE_SO_TOLERANCE_STATE; }) \
903 SO_INFRASTRUCTURE_CODE(if (__state.DidCatchSO()) { HANDLE_STACKOVERFLOW_AFTER_CATCH; }) \
904 NO_SO_INFRASTRUCTURE_CODE_ASSERTE(!__state.DidCatchSO()) \
905
906
907// CLRException::GetErrorInfo below invokes GetComIPFromObjectRef
908// that invokes ObjHeader::GetSyncBlock which has the INJECT_FAULT contract.
909//
910// This EX_CATCH_HRESULT implementation can be used in functions
911// that have FORBID_FAULT contracts.
912//
913// However, failure due to OOM (or any other potential exception) in GetErrorInfo
914// implies that we couldnt get the interface pointer from the objectRef and would be
915// returned NULL.
916//
917// Thus, the scoped use of FAULT_NOT_FATAL macro.
918#undef EX_CATCH_HRESULT
919#define EX_CATCH_HRESULT(_hr) \
920 EX_CATCH \
921 { \
922 (_hr) = GET_EXCEPTION()->GetHR(); \
923 { \
924 FAULT_NOT_FATAL(); \
925 HRESULT hrErrorInfo = GET_EXCEPTION()->SetErrorInfo(); \
926 if (FAILED(hrErrorInfo)) \
927 { \
928 (_hr) = hrErrorInfo; \
929 } \
930 } \
931 _ASSERTE(FAILED(_hr)); \
932 } \
933 EX_END_CATCH(SwallowAllExceptions)
934
935#endif // !DACCESS_COMPILE && !CROSSGEN_COMPILE
936
937// When collecting dumps, we need to ignore errors unless the user cancels.
938#define EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED \
939 EX_CATCH \
940 { \
941 /* Swallow the exception and keep going unless COR_E_OPERATIONCANCELED */ \
942 /* was thrown. Used generating dumps, where rethrow will cancel dump. */ \
943 } \
944 EX_END_CATCH(RethrowCancelExceptions)
945
946// Only use this version to wrap single source lines, or it makes debugging painful.
947#define CATCH_ALL_EXCEPT_RETHROW_COR_E_OPERATIONCANCELLED(sourceCode) \
948 EX_TRY \
949 { \
950 sourceCode \
951 } \
952 EX_CATCH_RETHROW_ONLY_COR_E_OPERATIONCANCELLED
953
954
955//==============================================================================
956// High-level macros for common uses of EX_TRY. Try using these rather
957// than the raw EX_TRY constructs.
958//==============================================================================
959
960//===================================================================================
961// Macro for defining external entrypoints such as COM interop boundaries.
962// The boundary will catch all exceptions (including terminals) and convert
963// them into HR/IErrorInfo pairs as appropriate.
964//
965// Usage:
966//
967// HRESULT hr; ;; BEGIN will initialize HR
968// BEGIN_EXTERNAL_ENTRYPOINT(&hr)
969// <do managed stuff> ;; this part will execute in cooperative GC mode
970// END_EXTERNAL_ENTRYPOINT
971// return hr;
972//
973// Comments:
974// The BEGIN macro will setup a Thread if necessary. It should only be called
975// in preemptive mode. If you are calling it from cooperative mode, this implies
976// we are executing "external" code in cooperative mode. The Reentrancy MDA
977// complains about this.
978//
979// Only use this macro for actual boundaries between CLR and
980// outside unmanaged code. If you want to connect internal pieces
981// of CLR code, use EX_TRY instead.
982//===================================================================================
983#ifdef MDA_SUPPORTED
984NOINLINE BOOL HasIllegalReentrancyRare();
985#define HAS_ILLEGAL_REENTRANCY() (NULL != MDA_GET_ASSISTANT(Reentrancy) && HasIllegalReentrancyRare())
986#else
987#define HAS_ILLEGAL_REENTRANCY() false
988#endif
989
990#define BEGIN_EXTERNAL_ENTRYPOINT(phresult) \
991 { \
992 HRESULT *__phr = (phresult); \
993 *__phr = S_OK; \
994 _ASSERTE(GetThread() == NULL || \
995 !GetThread()->PreemptiveGCDisabled()); \
996 if (HAS_ILLEGAL_REENTRANCY()) \
997 { \
998 *__phr = COR_E_ILLEGAL_REENTRANCY; \
999 } \
1000 else \
1001 if (!CanRunManagedCode()) \
1002 { \
1003 *__phr = E_PROCESS_SHUTDOWN_REENTRY; \
1004 } \
1005 else \
1006 { \
1007 MAKE_CURRENT_THREAD_AVAILABLE_EX(GetThreadNULLOk()); \
1008 if (CURRENT_THREAD == NULL) \
1009 { \
1010 CURRENT_THREAD = SetupThreadNoThrow(__phr); \
1011 } \
1012 if (CURRENT_THREAD != NULL) \
1013 { \
1014 BEGIN_SO_INTOLERANT_CODE_NOTHROW(CURRENT_THREAD, *__phr = COR_E_STACKOVERFLOW); \
1015 EX_TRY_THREAD(CURRENT_THREAD); \
1016 { \
1017
1018#define END_EXTERNAL_ENTRYPOINT \
1019 } \
1020 EX_CATCH_HRESULT(*__phr); \
1021 END_SO_INTOLERANT_CODE; \
1022 } \
1023 } \
1024 } \
1025
1026// This macro should be used at the entry points (e.g. COM interop boundaries)
1027// where CE's are not expected to get swallowed.
1028#define END_EXTERNAL_ENTRYPOINT_RETHROW_CORRUPTING_EXCEPTIONS_EX(fCond) \
1029 } \
1030 EX_CATCH \
1031 { \
1032 *__phr = GET_EXCEPTION()->GetHR(); \
1033 } \
1034 EX_END_CATCH(RethrowCorruptingExceptionsEx(fCond)); \
1035 END_SO_INTOLERANT_CODE; \
1036 } \
1037 } \
1038 } \
1039
1040// This macro should be used at the entry points (e.g. COM interop boundaries)
1041// where CE's are not expected to get swallowed.
1042#define END_EXTERNAL_ENTRYPOINT_RETHROW_CORRUPTING_EXCEPTIONS \
1043 END_EXTERNAL_ENTRYPOINT_RETHROW_CORRUPTING_EXCEPTIONS_EX(TRUE)
1044
1045
1046
1047//==============================================================================
1048
1049// ---------------------------------------------------------------------------
1050// Inline implementations. Pay no attention to that man behind the curtain.
1051// ---------------------------------------------------------------------------
1052
1053inline CLRException::CLRException()
1054 : m_throwableHandle(NULL)
1055{
1056 LIMITED_METHOD_CONTRACT;
1057}
1058
1059inline void CLRException::SetThrowableHandle(OBJECTHANDLE throwable)
1060{
1061 STRESS_LOG1(LF_EH, LL_INFO100, "in CLRException::SetThrowableHandle: obj = %x\n", throwable);
1062 m_throwableHandle = throwable;
1063}
1064
1065inline EEException::EEException(RuntimeExceptionKind kind)
1066 : m_kind(kind)
1067{
1068 LIMITED_METHOD_CONTRACT;
1069}
1070
1071inline EEException::EEException(HRESULT hr)
1072 : m_kind(GetKindFromHR(hr))
1073{
1074 LIMITED_METHOD_CONTRACT;
1075}
1076
1077inline EEMessageException::EEMessageException(HRESULT hr)
1078 : EEException(GetKindFromHR(hr)),
1079 m_hr(hr),
1080 m_resID(0)
1081{
1082 WRAPPER_NO_CONTRACT;
1083
1084 m_arg1.Printf("%.8x", hr);
1085}
1086
1087inline EEMessageException::EEMessageException(HRESULT hr, bool fUseCOMException)
1088 : EEException(GetKindFromHR(hr, !fUseCOMException)),
1089 m_hr(hr),
1090 m_resID(0)
1091{
1092 WRAPPER_NO_CONTRACT;
1093
1094 m_arg1.Printf("%.8x", hr);
1095}
1096
1097//-----------------------------------------------------------------------------
1098// Constructor with lots of defaults (to 0 / null)
1099// kind -- "clr kind" of the exception
1100// resid -- resource id for message
1101// strings -- substitution text for message
1102inline EEMessageException::EEMessageException(RuntimeExceptionKind kind, UINT resID, LPCWSTR szArg1, LPCWSTR szArg2,
1103 LPCWSTR szArg3, LPCWSTR szArg4, LPCWSTR szArg5, LPCWSTR szArg6)
1104 : EEException(kind),
1105 m_hr(EEException::GetHRFromKind(kind)),
1106 m_resID(resID),
1107 m_arg1(szArg1),
1108 m_arg2(szArg2),
1109 m_arg3(szArg3),
1110 m_arg4(szArg4),
1111 m_arg5(szArg5),
1112 m_arg6(szArg6)
1113{
1114 WRAPPER_NO_CONTRACT;
1115}
1116
1117//-----------------------------------------------------------------------------
1118// Constructor with lots of defaults (to 0 / null)
1119// hr -- hresult that lead to this exception
1120// resid -- resource id for message
1121// strings -- substitution text for message
1122inline EEMessageException::EEMessageException(HRESULT hr, UINT resID, LPCWSTR szArg1, LPCWSTR szArg2, LPCWSTR szArg3,
1123 LPCWSTR szArg4, LPCWSTR szArg5, LPCWSTR szArg6)
1124 : EEException(GetKindFromHR(hr)),
1125 m_hr(hr),
1126 m_resID(resID),
1127 m_arg1(szArg1),
1128 m_arg2(szArg2),
1129 m_arg3(szArg3),
1130 m_arg4(szArg4),
1131 m_arg5(szArg5),
1132 m_arg6(szArg6)
1133{
1134}
1135
1136//-----------------------------------------------------------------------------
1137// Constructor with no defaults
1138// kind -- "clr kind" of the exception
1139// hr -- hresult that lead to this exception
1140// resid -- resource id for message
1141// strings -- substitution text for message
1142inline EEMessageException::EEMessageException(RuntimeExceptionKind kind, HRESULT hr, UINT resID, LPCWSTR szArg1,
1143 LPCWSTR szArg2, LPCWSTR szArg3, LPCWSTR szArg4, LPCWSTR szArg5,
1144 LPCWSTR szArg6)
1145 : EEException(kind),
1146 m_hr(hr),
1147 m_resID(resID),
1148 m_arg1(szArg1),
1149 m_arg2(szArg2),
1150 m_arg3(szArg3),
1151 m_arg4(szArg4),
1152 m_arg5(szArg5),
1153 m_arg6(szArg6)
1154{
1155 WRAPPER_NO_CONTRACT;
1156}
1157
1158
1159inline EEResourceException::EEResourceException(RuntimeExceptionKind kind, const SString &resourceName)
1160 : EEException(kind),
1161 m_resourceName(resourceName)
1162{
1163 WRAPPER_NO_CONTRACT;
1164}
1165
1166
1167inline EEFieldException::EEFieldException(FieldDesc *pField)
1168 : EEException(kFieldAccessException),
1169 m_pFD(pField),
1170 m_pAccessingMD(NULL),
1171 m_messageID(0)
1172{
1173 WRAPPER_NO_CONTRACT;
1174}
1175
1176inline EEFieldException::EEFieldException(FieldDesc *pField, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID)
1177 : EEException(kFieldAccessException),
1178 m_pFD(pField),
1179 m_pAccessingMD(pAccessingMD),
1180 m_additionalContext(additionalContext),
1181 m_messageID(messageID)
1182{
1183}
1184
1185inline EEMethodException::EEMethodException(MethodDesc *pMethod)
1186 : EEException(kMethodAccessException),
1187 m_pMD(pMethod),
1188 m_pAccessingMD(NULL),
1189 m_messageID(0)
1190{
1191 WRAPPER_NO_CONTRACT;
1192}
1193
1194inline EEMethodException::EEMethodException(MethodDesc *pMethod, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID)
1195 : EEException(kMethodAccessException),
1196 m_pMD(pMethod),
1197 m_pAccessingMD(pAccessingMD),
1198 m_additionalContext(additionalContext),
1199 m_messageID(messageID)
1200{
1201}
1202
1203inline EETypeAccessException::EETypeAccessException(MethodTable *pMT)
1204 : EEException(kTypeAccessException),
1205 m_pMT(pMT),
1206 m_pAccessingMD(NULL),
1207 m_messageID(0)
1208{
1209}
1210
1211inline EETypeAccessException::EETypeAccessException(MethodTable *pMT, MethodDesc *pAccessingMD, const SString &additionalContext, UINT messageID)
1212 : EEException(kTypeAccessException),
1213 m_pMT(pMT),
1214 m_pAccessingMD(pAccessingMD),
1215 m_additionalContext(additionalContext),
1216 m_messageID(messageID)
1217{
1218}
1219
1220inline EEArgumentException::EEArgumentException(RuntimeExceptionKind reKind, LPCWSTR pArgName,
1221 LPCWSTR wszResourceName)
1222 : EEException(reKind),
1223 m_argumentName(pArgName),
1224 m_resourceName(wszResourceName)
1225{
1226 WRAPPER_NO_CONTRACT;
1227}
1228
1229
1230class ObjrefException : public CLRException
1231{
1232 friend bool DebugIsEECxxExceptionPointer(void* pv);
1233
1234 public:
1235
1236 ObjrefException();
1237 ObjrefException(OBJECTREF throwable);
1238
1239 private:
1240 static const int c_type = 0x4F522020; // 'OR '
1241
1242 public:
1243 // Dynamic type query for catchers
1244 static int GetType() {LIMITED_METHOD_CONTRACT; return c_type; }
1245 virtual int GetInstanceType() { LIMITED_METHOD_CONTRACT; return c_type; }
1246 BOOL IsType(int type) { WRAPPER_NO_CONTRACT; return type == c_type || CLRException::IsType(type); }
1247
1248protected:
1249 virtual Exception *CloneHelper()
1250 {
1251 WRAPPER_NO_CONTRACT;
1252 return new ObjrefException();
1253 }
1254
1255 virtual Exception *DomainBoundCloneHelper();
1256};
1257
1258
1259class CLRLastThrownObjectException : public CLRException
1260{
1261 friend bool DebugIsEECxxExceptionPointer(void* pv);
1262
1263 public:
1264 CLRLastThrownObjectException();
1265
1266 private:
1267 static const int c_type = 0x4C544F20; // 'LTO '
1268
1269 public:
1270 // Dynamic type query for catchers
1271 static int GetType() {LIMITED_METHOD_CONTRACT; return c_type; }
1272 virtual int GetInstanceType() { LIMITED_METHOD_CONTRACT; return c_type; }
1273 BOOL IsType(int type) { WRAPPER_NO_CONTRACT; return type == c_type || CLRException::IsType(type); }
1274
1275 #if defined(_DEBUG)
1276 CLRLastThrownObjectException* Validate();
1277 #endif // _DEBUG
1278
1279 protected:
1280 virtual Exception *CloneHelper();
1281
1282 virtual Exception *DomainBoundCloneHelper();
1283
1284 virtual OBJECTREF CreateThrowable();
1285};
1286
1287// Returns true if the HRESULT maps to the RuntimeExceptionKind (hr => kind).
1288bool IsHRESULTForExceptionKind(HRESULT hr, RuntimeExceptionKind kind);
1289
1290#endif // _CLREX_H_
1291
1292