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 using System;
6 using System.Diagnostics;
7 using System.Runtime.CompilerServices;
8 
9 using Internal.Runtime;
10 using Internal.Runtime.Augments;
11 
12 namespace System.Runtime.InteropServices
13 {
14     /// <summary>
15     /// This class has all the helpers which are needed to provide the Exception support for WinRT and ClassicCOM
16     /// </summary>
17     public static unsafe partial class ExceptionHelpers
18     {
19         /// <summary>
20         ///  This class is a helper class to call into IRestrictedErrorInfo methods.
21         /// </summary>
22         private static class RestrictedErrorInfoHelper
23         {
GetErrorDetails(System.IntPtr pRestrictedErrorInfo, out string errMsg, out int hr, out string resErrMsg, out string errCapSid)24             internal static bool GetErrorDetails(System.IntPtr pRestrictedErrorInfo, out string errMsg, out int hr, out string resErrMsg, out string errCapSid)
25             {
26                 Debug.Assert(pRestrictedErrorInfo != IntPtr.Zero);
27                 IntPtr pErrDes, pResErrDes, pErrCapSid;
28 
29                 pErrDes = pResErrDes = pErrCapSid = IntPtr.Zero;
30                 int result;
31                 try
32                 {
33                     // Get the errorDetails associated with the restrictedErrorInfo.
34                     __com_IRestrictedErrorInfo* pComRestrictedErrorInfo = (__com_IRestrictedErrorInfo*)pRestrictedErrorInfo;
35                     result = CalliIntrinsics.StdCall__int(
36                        pComRestrictedErrorInfo->pVtable->pfnGetErrorDetails,
37                        pRestrictedErrorInfo,
38                        out pErrDes,
39                        out hr,
40                        out pResErrDes,
41                        out pErrCapSid);
42 
43                     if (result >= 0)
44                     {
45                         // RestrictedErrorInfo details can be used since the pRestrictedErrorInfo has the same hr value as the hr returned by the native code.
46                         errMsg = Interop.COM.ConvertBSTRToString(pErrDes);
47                         resErrMsg = Interop.COM.ConvertBSTRToString(pResErrDes);
48                         errCapSid = Interop.COM.ConvertBSTRToString(pErrCapSid);
49                     }
50                     else
51                     {
52                         errMsg = resErrMsg = errCapSid = null;
53                         hr = 0;
54                     }
55                 }
56                 finally
57                 {
58                     if (pErrDes != IntPtr.Zero)
59                         ExternalInterop.SysFreeString(pErrDes);
60                     if (pResErrDes != IntPtr.Zero)
61                         ExternalInterop.SysFreeString(pResErrDes);
62                     if (pErrCapSid != IntPtr.Zero)
63                         ExternalInterop.SysFreeString(pErrCapSid);
64                 }
65 
66                 return result >= 0;
67             }
68 
GetReference(System.IntPtr pRestrictedErrorInfo, out string errReference)69             internal static void GetReference(System.IntPtr pRestrictedErrorInfo, out string errReference)
70             {
71                 Debug.Assert(pRestrictedErrorInfo != IntPtr.Zero);
72                 IntPtr pReference = IntPtr.Zero;
73                 errReference = null;
74 
75                 try
76                 {
77                     __com_IRestrictedErrorInfo* pComRestrictedErrorInfo = (__com_IRestrictedErrorInfo*)pRestrictedErrorInfo;
78                     int result = CalliIntrinsics.StdCall__int(pComRestrictedErrorInfo->pVtable->pfnGetReference, pRestrictedErrorInfo, out pReference);
79                     if (result >= 0)
80                     {
81                         errReference = Interop.COM.ConvertBSTRToString(pReference);
82                     }
83                     else
84                     {
85                         errReference = null;
86                     }
87                 }
88                 finally
89                 {
90                     if (pReference != IntPtr.Zero)
91                         ExternalInterop.SysFreeString(pReference);
92                 }
93             }
94         }
95 
96         /// <summary>
97         /// The method calls RoOriginateLanguageException. The method has all the logic in try, catch block to ensure that none of the exception helpers
98         /// throw exception themselves.
99         /// </summary>
100         /// <param name="ex"></param>
101         /// <returns></returns>
OriginateLanguageException(Exception ex)102         private static bool OriginateLanguageException(Exception ex)
103         {
104             IntPtr pUnk = IntPtr.Zero;
105             HSTRING errorMsg = default(HSTRING);
106             try
107             {
108                 pUnk = McgMarshal.ObjectToComInterface(ex, InternalTypes.IUnknown);
109                 if (pUnk != IntPtr.Zero)
110                 {
111                     RuntimeAugments.GenerateExceptionInformationForDump(ex, pUnk);
112 
113                     errorMsg = McgMarshal.StringToHString(ex.Message);
114 
115                     return ExternalInterop.RoOriginateLanguageException(ex.HResult, errorMsg, pUnk) >= 0;
116                 }
117             }
118             catch (Exception)
119             {
120                 // We can't do anything here and hence simply swallow the exception
121             }
122             finally
123             {
124                 McgMarshal.ComSafeRelease(pUnk);
125                 if (errorMsg.handle != IntPtr.Zero)
126                 {
127                     ExternalInterop.WindowsDeleteString(errorMsg.handle.ToPointer());
128                 }
129             }
130             return false;
131         }
132 
133 #pragma warning disable 649, 169  // Field 'blah' is never assigned to/Field 'blah' is never used
134 
135         // Lets create vTables for these interfaces.
136         private unsafe struct __com_ILanguageExceptionErrorInfo
137         {
138             internal __vtable_ILanguageExceptionErrorInfo* pVtable;
139         }
140 
141         private unsafe struct __vtable_ILanguageExceptionErrorInfo
142         {
143             private IntPtr pfnQueryInterface;
144             private IntPtr pfnAddRef;
145             private IntPtr pfnRelease;
146             internal System.IntPtr pfnGetLanguageException;
147         }
148 
149         internal unsafe struct __com_IRestrictedErrorInfo
150         {
151             internal __vtable_IRestrictedErrorInfo* pVtable;
152         }
153 
154         internal unsafe struct __vtable_IRestrictedErrorInfo
155         {
156             private IntPtr pfnQueryInterface;
157             private IntPtr pfnAddRef;
158             private IntPtr pfnRelease;
159             internal System.IntPtr pfnGetErrorDetails;
160             internal System.IntPtr pfnGetReference;
161         }
162 
163 #pragma warning restore 649, 169
164 
165         /// <summary>
166         /// This method gets the mapping hr for the exception. and also does the right thing to propogate the hr correctly to the native layer.
167         ///
168         /// We check if the exception is a pure managed exception or an exception created from an hr that entered the system from native.
169         /// a. If it is a pure managed exception we create an IUnknown ptr from the exception and RoOriginateLanguageException on it.
170         ///    This helps us to preserve our managed exception and throw the same exception in case this exception roundtrips and hence preserve the call stack.
171         ///    Since the API RoOriginateLanguageException is available only on windows blue, we can't do the same in win8. In desktop CLR we use the non-modern SDK API
172         ///    GetErroInfo\SetErrorInfo combination to preserve managed exception but unfortunately we can't do this in .NET Native and hence we only our able to preserve the exception message and
173         ///    type and end up getting a rough stacktrace PS - Even this behavior in win8 is possible only in debug mode as RoSetErrorReportingFlags is set to UseSetErrorInfo only in debug mode.
174         ///
175         /// b. In case the exception is created due to an hr that entered managed world via native call, we will have restrictederrorInfo associated with it. In this case
176         ///    we do not RoOriginateLanguageException\RoOriginateError and rather preserve the exception stack trace by simply calling the SetRestrictedErrorInfo.
177         ///
178         /// c. PS - Due to the use of modern SDK we have no way to round trip exceptions in classicCOM scenarios any more.
179         ///     This is because we can't use SetErrorInfo\GetErrorInfo APIs at all. Unfortunately we have no workaround for this even in windowsBlue!
180         ///    With the use of IRestrictedErrorInfo has some disadvantages as we lose other info available with IErrorInfo in terms of HelpFile etc.
181         ///
182         /// d. This class puts all the logic in try, catch block to ensure that none of the exception helpers.
183         ///  throw exception themselves.
184         /// </summary>
185         /// <param name="ex"></param>
186         /// <param name="isWinRTScenario"></param>
187         /// <returns></returns>
GetHRForExceptionWithErrorPropogationNoThrow(Exception ex, bool isWinRTScenario)188         internal static int GetHRForExceptionWithErrorPropogationNoThrow(Exception ex, bool isWinRTScenario)
189         {
190             int hr = ex.HResult;
191 
192             if (hr == Interop.COM.COR_E_OBJECTDISPOSED && isWinRTScenario)
193             {
194                 // Since ObjectDisposedException is projected to RO_E_CLOSED in WINRT we make sure to use the correct hr while updating the CRuntimeError object of Windows.
195                 hr = Interop.COM.RO_E_CLOSED;
196             }
197 
198             try
199             {
200                 // Check whether the exception has an associated RestrictedErrorInfo associated with it.
201                 if (isWinRTScenario)
202                 {
203                     IntPtr pRestrictedErrorInfo;
204                     object restrictedErrorInfo;
205                     if (InteropExtensions.TryGetRestrictedErrorObject(ex, out restrictedErrorInfo) && restrictedErrorInfo != null)
206                     {
207                         // We have the restricted errorInfo associated with this object and hence this exception was created by an hr entering managed through native.
208                         pRestrictedErrorInfo = McgMarshal.ObjectToComInterface(restrictedErrorInfo, InternalTypes.IRestrictedErrorInfo);
209                         if (pRestrictedErrorInfo != IntPtr.Zero)
210                         {
211                             // We simply call SetRestrictedErrorInfo since we do not want to originate the exception again.
212                             ExternalInterop.SetRestrictedErrorInfo(pRestrictedErrorInfo);
213                             McgMarshal.ComSafeRelease(pRestrictedErrorInfo);
214                         }
215                     }
216                     else
217                     {
218                         // we are in windows blue and hence we can preserve our exception so that we can reuse this exception in case it comes back and provide richer exception support.
219                         OriginateLanguageException(ex);
220                     }
221                 }
222                 else
223                 {
224                     // We are either pre WinBlue or in classicCOM scenario and hence we can only RoOriginateError at this point.
225                     // Desktop CLR uses SetErrorInfo and preserves the exception object which helps us give the same support as winBlue.
226                     // Since .NET Native can only use modern SDK we have a compatibility break here by only preserving the restrictederrorMsg and exception type but the stack trace will be incorrect.
227 
228                     // Also RoOriginateError works only under the debugger since RoSetErrorReportingFlags is set to RO_ERROR_REPORTING_USESETERRORINFO.
229                     // If we are not under the debugger we can't set this API since it is not part of the modernSDK and hence this will not work
230                     // and will result in different behavior than the desktop.
231                     HSTRING errorMsg = McgMarshal.StringToHString(ex.Message);
232                     ExternalInterop.RoOriginateError(ex.HResult, errorMsg);
233                     ExternalInterop.WindowsDeleteString(errorMsg.handle.ToPointer());
234                 }
235             }
236             catch (Exception)
237             {
238                 // We can't throw an exception here and hence simply swallow it.
239             }
240 
241             return hr;
242         }
243 
244         /// <summary>
245         /// This does a mapping from hr to the exception and also takes care of making default exception in case of classic COM as COMException.
246         /// and in winrt and marshal APIs as Exception.
247         /// </summary>
248         /// <param name="errorCode"></param>
249         /// <param name="message"></param>
250         /// <param name="createCOMException"></param>
251         /// <returns></returns>
GetMappingExceptionForHR(int errorCode, string message, bool createCOMException, bool hasErrorInfo)252         internal static Exception GetMappingExceptionForHR(int errorCode, string message, bool createCOMException, bool hasErrorInfo)
253         {
254             if (errorCode >= 0)
255             {
256                 return null;
257             }
258 
259             Exception exception = null;
260 
261             bool shouldDisplayHR = false;
262 
263             switch (errorCode)
264             {
265                 case __HResults.COR_E_NOTFINITENUMBER: // NotFiniteNumberException
266                 case __HResults.COR_E_ARITHMETIC:
267                     exception = new ArithmeticException();
268                     break;
269                 case __HResults.COR_E_ARGUMENT:
270                 case unchecked((int)0x800A01C1):
271                 case unchecked((int)0x800A01C2):
272                 case __HResults.CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT:
273                     exception = new ArgumentException();
274 
275                     if (errorCode != __HResults.COR_E_ARGUMENT)
276                         shouldDisplayHR = true;
277 
278                     break;
279                 case __HResults.E_BOUNDS:
280                 case __HResults.COR_E_ARGUMENTOUTOFRANGE:
281                 case __HResults.ERROR_NO_UNICODE_TRANSLATION:
282                     exception = new ArgumentOutOfRangeException();
283 
284                     if (errorCode != __HResults.COR_E_ARGUMENTOUTOFRANGE)
285                         shouldDisplayHR = true;
286 
287                     break;
288                 case __HResults.COR_E_ARRAYTYPEMISMATCH:
289                     exception = new ArrayTypeMismatchException();
290                     break;
291                 case __HResults.COR_E_BADIMAGEFORMAT:
292                 case __HResults.CLDB_E_FILE_OLDVER:
293                 case __HResults.CLDB_E_INDEX_NOTFOUND:
294                 case __HResults.CLDB_E_FILE_CORRUPT:
295                 case __HResults.COR_E_NEWER_RUNTIME:
296                 case __HResults.COR_E_ASSEMBLYEXPECTED:
297                 case __HResults.ERROR_BAD_EXE_FORMAT:
298                 case __HResults.ERROR_EXE_MARKED_INVALID:
299                 case __HResults.CORSEC_E_INVALID_IMAGE_FORMAT:
300                 case __HResults.ERROR_NOACCESS:
301                 case __HResults.ERROR_INVALID_ORDINAL:
302                 case __HResults.ERROR_INVALID_DLL:
303                 case __HResults.ERROR_FILE_CORRUPT:
304                 case __HResults.COR_E_LOADING_REFERENCE_ASSEMBLY:
305                 case __HResults.META_E_BAD_SIGNATURE:
306                     exception = new BadImageFormatException();
307 
308                     // Always show HR for BadImageFormatException
309                     shouldDisplayHR = true;
310 
311                     break;
312                 case __HResults.COR_E_CUSTOMATTRIBUTEFORMAT:
313                     exception = new FormatException();
314                     break; // CustomAttributeFormatException
315                 case __HResults.COR_E_DATAMISALIGNED:
316                     exception = InteropExtensions.CreateDataMisalignedException(message); // TODO: Do we need to add msg here?
317                     break;
318                 case __HResults.COR_E_DIVIDEBYZERO:
319                 case __HResults.CTL_E_DIVISIONBYZERO:
320                     exception = new DivideByZeroException();
321 
322                     if (errorCode != __HResults.COR_E_DIVIDEBYZERO)
323                         shouldDisplayHR = true;
324 
325                     break;
326                 case __HResults.COR_E_DLLNOTFOUND:
327 #if ENABLE_WINRT
328                     exception = new DllNotFoundException();
329 #endif
330                     break;
331                 case __HResults.COR_E_DUPLICATEWAITOBJECT:
332                     exception = new ArgumentException();
333                     break; // DuplicateWaitObjectException
334                 case __HResults.COR_E_ENDOFSTREAM:
335                 case unchecked((int)0x800A003E):
336                     exception = new System.IO.EndOfStreamException();
337 
338                     if (errorCode != __HResults.COR_E_ENDOFSTREAM)
339                         shouldDisplayHR = true;
340 
341                     break;
342                 case __HResults.COR_E_TYPEACCESS: // TypeAccessException
343                 case __HResults.COR_E_ENTRYPOINTNOTFOUND:
344                     exception = new TypeLoadException();
345 
346                     break; // EntryPointNotFoundException
347                 case __HResults.COR_E_EXCEPTION:
348                     exception = new Exception();
349                     break;
350                 case __HResults.COR_E_DIRECTORYNOTFOUND:
351                 case __HResults.STG_E_PATHNOTFOUND:
352                 case __HResults.CTL_E_PATHNOTFOUND:
353                     exception = new System.IO.DirectoryNotFoundException();
354 
355                     if (errorCode != __HResults.COR_E_DIRECTORYNOTFOUND)
356                         shouldDisplayHR = true;
357 
358                     break;
359                 case __HResults.COR_E_FILELOAD:
360                 case __HResults.FUSION_E_INVALID_PRIVATE_ASM_LOCATION:
361                 case __HResults.FUSION_E_SIGNATURE_CHECK_FAILED:
362                 case __HResults.FUSION_E_LOADFROM_BLOCKED:
363                 case __HResults.FUSION_E_CACHEFILE_FAILED:
364                 case __HResults.FUSION_E_ASM_MODULE_MISSING:
365                 case __HResults.FUSION_E_INVALID_NAME:
366                 case __HResults.FUSION_E_PRIVATE_ASM_DISALLOWED:
367                 case __HResults.FUSION_E_HOST_GAC_ASM_MISMATCH:
368                 case __HResults.COR_E_MODULE_HASH_CHECK_FAILED:
369                 case __HResults.FUSION_E_REF_DEF_MISMATCH:
370                 case __HResults.SECURITY_E_INCOMPATIBLE_SHARE:
371                 case __HResults.SECURITY_E_INCOMPATIBLE_EVIDENCE:
372                 case __HResults.SECURITY_E_UNVERIFIABLE:
373                 case __HResults.COR_E_FIXUPSINEXE:
374                 case __HResults.ERROR_TOO_MANY_OPEN_FILES:
375                 case __HResults.ERROR_SHARING_VIOLATION:
376                 case __HResults.ERROR_LOCK_VIOLATION:
377                 case __HResults.ERROR_OPEN_FAILED:
378                 case __HResults.ERROR_DISK_CORRUPT:
379                 case __HResults.ERROR_UNRECOGNIZED_VOLUME:
380                 case __HResults.ERROR_DLL_INIT_FAILED:
381                 case __HResults.FUSION_E_CODE_DOWNLOAD_DISABLED:
382                 case __HResults.CORSEC_E_MISSING_STRONGNAME:
383                 case __HResults.MSEE_E_ASSEMBLYLOADINPROGRESS:
384                 case __HResults.ERROR_FILE_INVALID:
385                     exception = new System.IO.FileLoadException();
386 
387                     shouldDisplayHR = true;
388                     break;
389                 case __HResults.COR_E_PATHTOOLONG:
390                     exception = new System.IO.PathTooLongException();
391                     break;
392                 case __HResults.COR_E_IO:
393                 case __HResults.CTL_E_DEVICEIOERROR:
394                 case unchecked((int)0x800A793C):
395                 case unchecked((int)0x800A793D):
396                     exception = new System.IO.IOException();
397 
398                     if (errorCode != __HResults.COR_E_IO)
399                         shouldDisplayHR = true;
400 
401                     break;
402                 case __HResults.ERROR_FILE_NOT_FOUND:
403                 case __HResults.ERROR_MOD_NOT_FOUND:
404                 case __HResults.ERROR_INVALID_NAME:
405                 case __HResults.CTL_E_FILENOTFOUND:
406                 case __HResults.ERROR_BAD_NET_NAME:
407                 case __HResults.ERROR_BAD_NETPATH:
408                 case __HResults.ERROR_NOT_READY:
409                 case __HResults.ERROR_WRONG_TARGET_NAME:
410                 case __HResults.INET_E_UNKNOWN_PROTOCOL:
411                 case __HResults.INET_E_CONNECTION_TIMEOUT:
412                 case __HResults.INET_E_CANNOT_CONNECT:
413                 case __HResults.INET_E_RESOURCE_NOT_FOUND:
414                 case __HResults.INET_E_OBJECT_NOT_FOUND:
415                 case __HResults.INET_E_DOWNLOAD_FAILURE:
416                 case __HResults.INET_E_DATA_NOT_AVAILABLE:
417                 case __HResults.ERROR_DLL_NOT_FOUND:
418                 case __HResults.CLR_E_BIND_ASSEMBLY_VERSION_TOO_LOW:
419                 case __HResults.CLR_E_BIND_ASSEMBLY_PUBLIC_KEY_MISMATCH:
420                 case __HResults.CLR_E_BIND_ASSEMBLY_NOT_FOUND:
421                     exception = new System.IO.FileNotFoundException();
422 
423                     shouldDisplayHR = true;
424                     break;
425                 case __HResults.COR_E_FORMAT:
426                     exception = new FormatException();
427                     break;
428                 case __HResults.COR_E_INDEXOUTOFRANGE:
429                 case unchecked((int)0x800a0009):
430                     exception = new IndexOutOfRangeException();
431 
432                     if (errorCode != __HResults.COR_E_INDEXOUTOFRANGE)
433                         shouldDisplayHR = true;
434 
435                     break;
436                 case __HResults.COR_E_INVALIDCAST:
437                     exception = new InvalidCastException();
438                     break;
439                 case __HResults.COR_E_INVALIDCOMOBJECT:
440                     exception = new InvalidComObjectException();
441                     break;
442                 case __HResults.COR_E_INVALIDOLEVARIANTTYPE:
443                     exception = new InvalidOleVariantTypeException();
444                     break;
445                 case __HResults.COR_E_INVALIDOPERATION:
446                 case __HResults.E_ILLEGAL_STATE_CHANGE:
447                 case __HResults.E_ILLEGAL_METHOD_CALL:
448                 case __HResults.E_ILLEGAL_DELEGATE_ASSIGNMENT:
449                 case __HResults.APPMODEL_ERROR_NO_PACKAGE:
450                     exception = new InvalidOperationException();
451 
452                     if (errorCode != __HResults.COR_E_INVALIDOPERATION)
453                         shouldDisplayHR = true;
454 
455                     break;
456                 case __HResults.COR_E_MARSHALDIRECTIVE:
457                     exception = new MarshalDirectiveException();
458                     break;
459                 case __HResults.COR_E_METHODACCESS: // MethodAccessException
460                 case __HResults.META_E_CA_FRIENDS_SN_REQUIRED: // MethodAccessException
461                 case __HResults.COR_E_FIELDACCESS:
462                 case __HResults.COR_E_MEMBERACCESS:
463                     exception = new MemberAccessException();
464 
465                     if (errorCode != __HResults.COR_E_METHODACCESS)
466                         shouldDisplayHR = true;
467 
468                     break;
469                 case __HResults.COR_E_MISSINGFIELD: // MissingFieldException
470                 case __HResults.COR_E_MISSINGMETHOD: // MissingMethodException
471                 case __HResults.COR_E_MISSINGMEMBER:
472                 case unchecked((int)0x800A01CD):
473                     exception = new MissingMemberException();
474                     break;
475                 case __HResults.COR_E_MISSINGMANIFESTRESOURCE:
476                     exception = new System.Resources.MissingManifestResourceException();
477                     break;
478                 case __HResults.COR_E_NOTSUPPORTED:
479                 case unchecked((int)0x800A01B6):
480                 case unchecked((int)0x800A01BD):
481                 case unchecked((int)0x800A01CA):
482                 case unchecked((int)0x800A01CB):
483                     exception = new NotSupportedException();
484 
485                     if (errorCode != __HResults.COR_E_NOTSUPPORTED)
486                         shouldDisplayHR = true;
487 
488                     break;
489                 case __HResults.COR_E_NULLREFERENCE:
490                     exception = new NullReferenceException();
491                     break;
492                 case __HResults.COR_E_OBJECTDISPOSED:
493                 case __HResults.RO_E_CLOSED:
494                     // No default constructor
495                     exception = new ObjectDisposedException(String.Empty);
496                     break;
497                 case __HResults.COR_E_OPERATIONCANCELED:
498 #if ENABLE_WINRT
499                     exception = new OperationCanceledException();
500 #endif
501                     break;
502                 case __HResults.COR_E_OVERFLOW:
503                 case __HResults.CTL_E_OVERFLOW:
504                     exception = new OverflowException();
505                     break;
506                 case __HResults.COR_E_PLATFORMNOTSUPPORTED:
507                     exception = new PlatformNotSupportedException(message);
508                     break;
509                 case __HResults.COR_E_RANK:
510                     exception = new RankException();
511                     break;
512                 case __HResults.COR_E_REFLECTIONTYPELOAD:
513 #if ENABLE_WINRT
514                     exception = new System.Reflection.ReflectionTypeLoadException(null, null);
515 #endif
516                     break;
517                 case __HResults.COR_E_SECURITY:
518                 case __HResults.CORSEC_E_INVALID_STRONGNAME:
519                 case __HResults.CTL_E_PERMISSIONDENIED:
520                 case unchecked((int)0x800A01A3):
521                 case __HResults.CORSEC_E_INVALID_PUBLICKEY:
522                 case __HResults.CORSEC_E_SIGNATURE_MISMATCH:
523                     exception = new System.Security.SecurityException();
524                     break;
525                 case __HResults.COR_E_SAFEARRAYRANKMISMATCH:
526                     exception = new SafeArrayRankMismatchException();
527                     break;
528                 case __HResults.COR_E_SAFEARRAYTYPEMISMATCH:
529                     exception = new SafeArrayTypeMismatchException();
530                     break;
531                 case __HResults.COR_E_SERIALIZATION:
532                     exception = new System.Runtime.Serialization.SerializationException(message);
533                     break;
534                 case __HResults.COR_E_SYNCHRONIZATIONLOCK:
535                     exception = new System.Threading.SynchronizationLockException();
536                     break;
537                 case __HResults.COR_E_TARGETINVOCATION:
538                     exception = new System.Reflection.TargetInvocationException(null);
539                     break;
540                 case __HResults.COR_E_TARGETPARAMCOUNT:
541                     exception = new System.Reflection.TargetParameterCountException();
542                     break;
543                 case __HResults.COR_E_TYPEINITIALIZATION:
544                     exception = InteropExtensions.CreateTypeInitializationException(message);
545                     break;
546                 case __HResults.COR_E_TYPELOAD:
547                 case __HResults.RO_E_METADATA_NAME_NOT_FOUND:
548                 case __HResults.CLR_E_BIND_TYPE_NOT_FOUND:
549                     exception = new TypeLoadException();
550 
551                     if (errorCode != __HResults.COR_E_TYPELOAD)
552                         shouldDisplayHR = true;
553 
554                     break;
555                 case __HResults.COR_E_UNAUTHORIZEDACCESS:
556                 case __HResults.CTL_E_PATHFILEACCESSERROR:
557                 case unchecked((int)0x800A014F):
558                     exception = new UnauthorizedAccessException();
559 
560                     shouldDisplayHR = true;
561 
562                     break;
563                 case __HResults.COR_E_VERIFICATION:
564                     exception = new System.Security.VerificationException();
565                     break;
566                 case __HResults.E_NOTIMPL:
567                     exception = NotImplemented.ByDesign;
568                     break;
569                 case __HResults.E_OUTOFMEMORY:
570                 case __HResults.CTL_E_OUTOFMEMORY:
571                 case unchecked((int)0x800A7919):
572                     exception = new OutOfMemoryException();
573 
574                     if (errorCode != __HResults.E_OUTOFMEMORY)
575                         shouldDisplayHR = true;
576 
577                     break;
578 #if ENABLE_WINRT
579                 case __HResults.E_XAMLPARSEFAILED:
580                     exception = ConstructExceptionUsingReflection(
581                         "Windows.UI.Xaml.Markup.XamlParseException, System.Runtime.WindowsRuntime.UI.Xaml, Version=4.0.0.0",
582                         message);
583                     break;
584                 case __HResults.E_ELEMENTNOTAVAILABLE:
585                     exception = ConstructExceptionUsingReflection(
586                         "Windows.UI.Xaml.Automation.ElementNotAvailableException, System.Runtime.WindowsRuntime.UI.Xaml, Version=4.0.0.0",
587                         message);
588                     break;
589                 case __HResults.E_ELEMENTNOTENABLED:
590                     exception = ConstructExceptionUsingReflection(
591                         "Windows.UI.Xaml.Automation.ElementNotEnabledException, System.Runtime.WindowsRuntime.UI.Xaml, Version=4.0.0.0",
592                         message);
593                     break;
594                 case __HResults.E_LAYOUTCYCLE:
595                     exception = ConstructExceptionUsingReflection(
596                         "Windows.UI.Xaml.LayoutCycleException, System.Runtime.WindowsRuntime.UI.Xaml, Version=4.0.0.0",
597                         message);
598                     break;
599 #endif // ENABLE_WINRT
600                 case __HResults.COR_E_AMBIGUOUSMATCH: // AmbiguousMatchException
601                 case __HResults.COR_E_APPLICATION: // ApplicationException
602                 case __HResults.COR_E_APPDOMAINUNLOADED: // AppDomainUnloadedException
603                 case __HResults.COR_E_CANNOTUNLOADAPPDOMAIN: // CannotUnloadAppDomainException
604                 case __HResults.COR_E_CODECONTRACTFAILED: // ContractException
605                 case __HResults.COR_E_CONTEXTMARSHAL: // ContextMarshalException
606                 case __HResults.CORSEC_E_CRYPTO: // CryptographicException
607                 case __HResults.CORSEC_E_CRYPTO_UNEX_OPER: // CryptographicUnexpectedOperationException
608                 case __HResults.COR_E_EXECUTIONENGINE: // ExecutionEngineException
609                 case __HResults.COR_E_INSUFFICIENTEXECUTIONSTACK: // InsufficientExecutionStackException
610                 case __HResults.COR_E_INVALIDFILTERCRITERIA: // InvalidFilterCriteriaException
611                 case __HResults.COR_E_INVALIDPROGRAM: // InvalidProgramException
612                 case __HResults.COR_E_MULTICASTNOTSUPPORTED: // MulticastNotSupportedException
613                 case __HResults.COR_E_REMOTING: // RemotingException
614                 case __HResults.COR_E_RUNTIMEWRAPPED: // RuntimeWrappedException
615                 case __HResults.COR_E_SERVER: // ServerException
616                 case __HResults.COR_E_STACKOVERFLOW: // StackOverflowException
617                 case __HResults.CTL_E_OUTOFSTACKSPACE: // StackOverflowException
618                 case __HResults.COR_E_SYSTEM: // SystemException
619                 case __HResults.COR_E_TARGET: // TargetException
620                 case __HResults.COR_E_THREADABORTED: // TargetException
621                 case __HResults.COR_E_THREADINTERRUPTED: // ThreadInterruptedException
622                 case __HResults.COR_E_THREADSTATE: // ThreadStateException
623                 case __HResults.COR_E_THREADSTART: // ThreadStartException
624                 case __HResults.COR_E_TYPEUNLOADED: // TypeUnloadedException
625                 case __HResults.CORSEC_E_POLICY_EXCEPTION: // PolicyException
626                 case __HResults.CORSEC_E_NO_EXEC_PERM: // PolicyException
627                 case __HResults.CORSEC_E_MIN_GRANT_FAIL: // PolicyException
628                 case __HResults.CORSEC_E_XMLSYNTAX: // XmlSyntaxException
629                 case __HResults.ISS_E_ALLOC_TOO_LARGE: // IsolatedStorageException
630                 case __HResults.ISS_E_BLOCK_SIZE_TOO_SMALL: // IsolatedStorageException
631                 case __HResults.ISS_E_CALLER: // IsolatedStorageException
632                 case __HResults.ISS_E_CORRUPTED_STORE_FILE: // IsolatedStorageException
633                 case __HResults.ISS_E_CREATE_DIR: // IsolatedStorageException
634                 case __HResults.ISS_E_CREATE_MUTEX: // IsolatedStorageException
635                 case __HResults.ISS_E_DEPRECATE: // IsolatedStorageException
636                 case __HResults.ISS_E_FILE_NOT_MAPPED: // IsolatedStorageException
637                 case __HResults.ISS_E_FILE_WRITE: // IsolatedStorageException
638                 case __HResults.ISS_E_GET_FILE_SIZE: // IsolatedStorageException
639                 case __HResults.ISS_E_ISOSTORE: // IsolatedStorageException
640                 case __HResults.ISS_E_LOCK_FAILED: // IsolatedStorageException
641                 case __HResults.ISS_E_MACHINE: // IsolatedStorageException
642                 case __HResults.ISS_E_MACHINE_DACL: // IsolatedStorageException
643                 case __HResults.ISS_E_MAP_VIEW_OF_FILE: // IsolatedStorageException
644                 case __HResults.ISS_E_OPEN_FILE_MAPPING: // IsolatedStorageException
645                 case __HResults.ISS_E_OPEN_STORE_FILE: // IsolatedStorageException
646                 case __HResults.ISS_E_PATH_LENGTH: // IsolatedStorageException
647                 case __HResults.ISS_E_SET_FILE_POINTER: // IsolatedStorageException
648                 case __HResults.ISS_E_STORE_NOT_OPEN: // IsolatedStorageException
649                 case __HResults.ISS_E_STORE_VERSION: // IsolatedStorageException
650                 case __HResults.ISS_E_TABLE_ROW_NOT_FOUND: // IsolatedStorageException
651                 case __HResults.ISS_E_USAGE_WILL_EXCEED_QUOTA: // IsolatedStorageException
652                 case __HResults.E_FAIL:
653                 default:
654                     break;
655             }
656 
657             if (exception == null)
658             {
659                 if (createCOMException)
660                 {
661                     exception = new COMException();
662                     if (errorCode != __HResults.E_FAIL)
663                         shouldDisplayHR = true;
664                 }
665                 else
666                 {
667                     exception = new Exception();
668                     if (errorCode != __HResults.COR_E_EXCEPTION)
669                         shouldDisplayHR = true;
670                  }
671             }
672 
673             bool shouldConstructMessage = false;
674             if (hasErrorInfo)
675             {
676                 // If there is a IErrorInfo/IRestrictedErrorInfo, only construct a new error message if
677                 // the message is not available and do not use the shouldDisplayHR setting
678                 if (message == null)
679                     shouldConstructMessage = true;
680             }
681             else
682             {
683                 // If there is no IErrorInfo, use the shouldDisplayHR setting from the big switch/case above
684                 shouldConstructMessage = shouldDisplayHR;
685             }
686 
687             if (shouldConstructMessage)
688             {
689                 //
690                 // Append the HR into error message, just in case the app wants to look at the HR in
691                 // message to determine behavior.  We didn't expose HResult property until v4.5 and
692                 // GetHRFromException has side effects so probably Message was their only choice.
693                 // This behavior is probably not exactly the same as in desktop but it is fine to append
694                 // more message at the end. In any case, having the HR in the error message are helpful
695                 // to developers.
696                 // This makes sure:
697                 // 1. We always have a HR 0xNNNNNNNN in the message
698                 // 2. Put in a nice "Exception thrown from HRESULT" message if we can
699                 // 3. Wrap it in () if there is an existing message
700                 //
701 
702                 // TODO: Add Symbolic Name into Messaage, convert 0x80020006 to DISP_E_UNKNOWNNAME
703                 string hrMessage = String.Format("{0} 0x{1}", SR.Excep_FromHResult, errorCode.LowLevelToString());
704 
705                 message = ExternalInterop.GetMessage(errorCode);
706 
707                 // Always make sure we have at least the HRESULT part in retail build or when the message
708                 // is empty.
709                 if (message == null)
710                     message = hrMessage;
711                 else
712                     message = message + " (" + hrMessage + ")";
713             }
714 
715             if (message != null)
716             {
717                 // Set message explicitly rather than calling constructor because certain ctors would append a
718                 // prefix to the message and that is not what we want
719                 InteropExtensions.SetExceptionMessage(exception, message);
720             }
721 
722             InteropExtensions.SetExceptionErrorCode(exception, errorCode);
723 
724             return exception;
725         }
726 
727         /// <summary>
728         /// Construct exception dynamically using reflection.
729         /// </summary>
730         /// <param name="exceptionTypeName">Assembly-qualified exception type name</param>
731         /// <param name="message">Message to use for exception creation (null = use parameterless constructor)</param>
ConstructExceptionUsingReflection(string exceptionTypeName, string message)732         static Exception ConstructExceptionUsingReflection(string exceptionTypeName, string message)
733         {
734             Exception result = null;
735 
736             try
737             {
738                 Type exceptionType = Type.GetType(exceptionTypeName);
739 
740                 if (exceptionType != null)
741                 {
742                     if (message == null)
743                     {
744                         result = (Exception)Activator.CreateInstance(exceptionType);
745                     }
746                     else
747                     {
748                         result = (Exception)Activator.CreateInstance(exceptionType, message);
749                     }
750                 }
751             }
752             catch (Exception)
753             {
754                 // Ignore exceptions during exception construction - a default exception will be returned
755             }
756             return result;
757         }
758 
759         [MethodImplAttribute(MethodImplOptions.NoInlining)]
TryGetRestrictedErrorInfo(out IntPtr pRestrictedErrorInfo)760         static bool TryGetRestrictedErrorInfo(out IntPtr pRestrictedErrorInfo)
761         {
762             return ExternalInterop.GetRestrictedErrorInfo(out pRestrictedErrorInfo) >= 0 && pRestrictedErrorInfo != IntPtr.Zero;
763         }
764 
765         /// <summary>
766         /// This method returns a new Exception object given the HR value.
767         ///
768         /// 1. We check whether we have our own LanguageException associated with this hr. If so we simply use it since it helps preserve the stacktrace, message and type.
769         ///    This is done using GetLanguageException API on ILanguageExceptionErrorInfo from IRestrictedErrorInfo. Since ILanguageExceptionErrorInfo is available only on Windows Blue
770         ///    we can only do this WindowsBlue and above. In desktop CLR we could use GetErroInfo and check whether we have our IErroInfo and retrieve our own exception.
771         ///    For Win8 in .NET Native we simply create the exception using the RestrictedErrorInfo and hence only able to give the exception with restrictedErrorMsg.
772         /// 2. In case we do not have the languageException we simply check RestrictedErrorInfo for errorMsg and create an exception using
773         ///   <errorMsg>\r\n<restrictedErrorMsg>. This is done for only windows blue. To be backward compatible we only use errorMsg for creating exception in win8.
774         /// 3. PS - This class puts all the logic in try, catch block to ensure that none of the exception helpers
775         ///  throw exception themselves.
776         /// </summary>
777         /// <param name="hr"></param>
778         /// <param name="isWinRTScenario"></param>
GetExceptionForHRInternalNoThrow(int hr, bool isWinRTScenario, bool isClassicCOM)779         internal static Exception GetExceptionForHRInternalNoThrow(int hr, bool isWinRTScenario, bool isClassicCOM)
780         {
781             Exception ex;
782             IntPtr pRestrictedErrorInfo = IntPtr.Zero;
783 
784             try
785             {
786                 if (TryGetRestrictedErrorInfo(out pRestrictedErrorInfo))
787                 {
788                     // This is to check whether we need to give post win8 behavior or not.
789                     if (isWinRTScenario)
790                     {
791                         // Check whether the given IRestrictedErrorInfo object supports ILanguageExceptionErrorInfo
792                         IntPtr pLanguageExceptionErrorInfo = McgMarshal.ComQueryInterfaceNoThrow(pRestrictedErrorInfo, ref Interop.COM.IID_ILanguageExceptionErrorInfo);
793                         if (pLanguageExceptionErrorInfo != IntPtr.Zero)
794                         {
795                             // We have an LanguageExceptionErrorInfo.
796                             IntPtr pUnk;
797                             __com_ILanguageExceptionErrorInfo* pComLanguageExceptionErrorInfo = (__com_ILanguageExceptionErrorInfo*)pLanguageExceptionErrorInfo;
798                             int result = CalliIntrinsics.StdCall__int(pComLanguageExceptionErrorInfo->pVtable->pfnGetLanguageException, pLanguageExceptionErrorInfo, out pUnk);
799                             McgMarshal.ComSafeRelease(pLanguageExceptionErrorInfo);
800 
801                             if (result >= 0 && pUnk != IntPtr.Zero)
802                             {
803                                 try
804                                 {
805                                     // Check whether the given pUnk is a managed exception.
806                                     ComCallableObject ccw;
807                                     if (ComCallableObject.TryGetCCW(pUnk, out ccw))
808                                     {
809                                         return ccw.TargetObject as Exception;
810                                     }
811                                 }
812                                 finally
813                                 {
814                                     McgMarshal.ComSafeRelease(pUnk);
815                                 }
816                             }
817                         }
818                     }
819                     String message = null, errorInfoReference = null;
820                     string errMsg, errCapSid, resErrMsg;
821                     int errHr;
822                     object restrictedErrorInfo = null;
823 
824                     bool hasErrorInfo = false;
825                     if (RestrictedErrorInfoHelper.GetErrorDetails(pRestrictedErrorInfo, out errMsg, out errHr, out resErrMsg, out errCapSid) && errHr == hr)
826                     {
827                         // RestrictedErrorInfo details can be used since the pRestrictedErrorInfo has the same hr value as the hr returned by the native code.
828                         // We are in windows blue or above and hence the exceptionMsg is errMsg + "\r\n" + resErrMsg
829                         message = String.IsNullOrEmpty(resErrMsg) ? errMsg : errMsg + "\r\n" + resErrMsg;
830                         RestrictedErrorInfoHelper.GetReference(pRestrictedErrorInfo, out errorInfoReference);
831                         restrictedErrorInfo = McgMarshal.ComInterfaceToObject(pRestrictedErrorInfo, InternalTypes.IRestrictedErrorInfo);
832 
833                         hasErrorInfo = true;
834                     }
835 
836                     if (hr == Interop.COM.RO_E_CLOSED && isWinRTScenario)
837                         hr = Interop.COM.COR_E_OBJECTDISPOSED;
838 
839                     // Now we simply need to set the description and the resDescription by adding an internal method.
840                     ex = GetMappingExceptionForHR(hr, message, isClassicCOM, hasErrorInfo);
841 
842                     if (restrictedErrorInfo != null)
843                     {
844                         InteropExtensions.AddExceptionDataForRestrictedErrorInfo(ex, resErrMsg, errorInfoReference, errCapSid, restrictedErrorInfo);
845                     }
846 
847                     return ex;
848                 }
849             }
850             catch (Exception)
851             {
852                 // We can't do any thing here and hence we swallow the exception and get the corresponding hr.
853             }
854             finally
855             {
856                 McgMarshal.ComSafeRelease(pRestrictedErrorInfo);
857             }
858 
859             // We could not find any restrictedErrorInfo associated with this object and hence we simply use the hr to create the exception.
860             return GetMappingExceptionForHR(hr, null, isClassicCOM, hasErrorInfo: false);
861         }
862 
ReportUnhandledError(Exception e)863         internal static bool ReportUnhandledError(Exception e)
864         {
865             System.IntPtr pRestrictedErrorInfo = IntPtr.Zero;
866 
867             if (e != null)
868             {
869                 try
870                 {
871 #if ENABLE_WINRT
872                     // Only report to the WinRT global exception handler in modern apps
873                     WinRTInteropCallbacks callbacks = WinRTInterop.Callbacks;
874                     if (callbacks == null || !callbacks.IsAppxModel())
875                     {
876                         return false;
877                     }
878 
879                     // Get the IUnknown for the current exception and originate it as a langauge error in order to have
880                     // Windows generate an IRestrictedErrorInfo corresponding to the exception object.  We can then
881                     // notify the global error handler that this IRestrictedErrorInfo instance represents an exception that
882                     // went unhandled in managed code.
883                     if (OriginateLanguageException(e) && TryGetRestrictedErrorInfo(out pRestrictedErrorInfo))
884                     {
885                         return ExternalInterop.RoReportUnhandledError(pRestrictedErrorInfo) >= 0;
886                     }
887 #else
888                     return false;
889 #endif // ENABLE_WINRT
890                 }
891                 catch (Exception)
892                 {
893                     // We can't give an exception in this code, so we simply swallow the exception here.
894                 }
895                 finally
896                 {
897                     McgMarshal.ComSafeRelease(pRestrictedErrorInfo);
898                 }
899             }
900             // If we have got here, then some step of the pInvoke failed, which means the GEH was not invoked
901             return false;
902         }
903 
AttachRestrictedErrorInfo(Exception e)904         internal static Exception AttachRestrictedErrorInfo(Exception e)
905         {
906             // If there is no exception, then the restricted error info doesn't apply to it
907             if (e != null)
908             {
909                 System.IntPtr pRestrictedErrorInfo = IntPtr.Zero;
910 
911                 try
912                 {
913                     // Get the restricted error info for this thread and see if it may correlate to the current
914                     // exception object.  Note that in general the thread's IRestrictedErrorInfo is not meant for
915                     // exceptions that are marshaled Windows.Foundation.HResults and instead are intended for
916                     // HRESULT ABI return values.   However, in many cases async APIs will set the thread's restricted
917                     // error info as a convention in order to provide extended debugging information for the ErrorCode
918                     // property.
919                     if (TryGetRestrictedErrorInfo(out pRestrictedErrorInfo))
920                     {
921                         string description;
922                         string restrictedDescription;
923                         string capabilitySid;
924                         int restrictedErrorInfoHResult;
925                         if (RestrictedErrorInfoHelper.GetErrorDetails(
926                                                             pRestrictedErrorInfo,
927                                                             out description,
928                                                             out restrictedErrorInfoHResult,
929                                                             out restrictedDescription,
930                                                             out capabilitySid) && (e.HResult == restrictedErrorInfoHResult))
931                         {
932                             // Since this is a special case where by convention there may be a correlation, there is not a
933                             // guarantee that the restricted error info does belong to the async error code.  In order to
934                             // reduce the risk that we associate incorrect information with the exception object, we need
935                             // to apply a heuristic where we attempt to match the current exception's HRESULT with the
936                             // HRESULT the IRestrictedErrorInfo belongs to.  If it is a match we will assume association
937                             // for the IAsyncInfo case.
938                             string errorReference;
939                             RestrictedErrorInfoHelper.GetReference(pRestrictedErrorInfo, out errorReference);
940                             object restrictedErrorInfo = McgMarshal.ComInterfaceToObject(pRestrictedErrorInfo, InternalTypes.IRestrictedErrorInfo);
941                             InteropExtensions.AddExceptionDataForRestrictedErrorInfo(
942                                                                                  e,
943                                                                                  restrictedDescription,
944                                                                                  errorReference,
945                                                                                  capabilitySid,
946                                                                                  restrictedErrorInfo);
947                         }
948                     }
949                 }
950                 catch (Exception)
951                 {
952                     // If we can't get the restricted error info, then proceed as if it isn't associated with this
953                     // error.
954                 }
955                 finally
956                 {
957                     McgMarshal.ComSafeRelease(pRestrictedErrorInfo);
958                 }
959             }
960             return e;
961         }
962     }
963 }
964