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