File: MS\Internal\WindowsRuntime\Generated\WinRT\ExceptionHelpers.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using WinRT.Interop;
 
namespace WinRT
{
    internal static class ExceptionHelpers
    {
        private const int COR_E_OBJECTDISPOSED = unchecked((int)0x80131622);
        private const int RO_E_CLOSED = unchecked((int)0x80000013);
        internal const int E_BOUNDS = unchecked((int)0x8000000b);
        internal const int E_CHANGED_STATE = unchecked((int)0x8000000c);
        private const int E_ILLEGAL_STATE_CHANGE = unchecked((int)0x8000000d);
        private const int E_ILLEGAL_METHOD_CALL = unchecked((int)0x8000000e);
        private const int E_ILLEGAL_DELEGATE_ASSIGNMENT = unchecked((int)0x80000018);
        private const int APPMODEL_ERROR_NO_PACKAGE = unchecked((int)0x80073D54);
        internal const int E_XAMLPARSEFAILED = unchecked((int)0x802B000A);
        internal const int E_LAYOUTCYCLE = unchecked((int)0x802B0014);
        internal const int E_ELEMENTNOTENABLED = unchecked((int)0x802B001E);
        internal const int E_ELEMENTNOTAVAILABLE = unchecked((int)0x802B001F);
 
        [DllImport("oleaut32.dll")]
        private static extern int SetErrorInfo(uint dwReserved, IntPtr perrinfo);
 
        internal delegate int GetRestrictedErrorInfo(out IntPtr ppRestrictedErrorInfo);
        private static GetRestrictedErrorInfo getRestrictedErrorInfo;
 
        internal delegate int SetRestrictedErrorInfo(IntPtr pRestrictedErrorInfo);
        private static SetRestrictedErrorInfo setRestrictedErrorInfo;
 
        internal delegate int RoOriginateLanguageException(int error, IntPtr message, IntPtr langaugeException);
        private static RoOriginateLanguageException roOriginateLanguageException;
 
        internal delegate int RoReportUnhandledError(IntPtr pRestrictedErrorInfo);
        private static RoReportUnhandledError roReportUnhandledError;
 
        static ExceptionHelpers()
        {
            IntPtr winRTErrorModule = Platform.LoadLibraryExW("api-ms-win-core-winrt-error-l1-1-1.dll", IntPtr.Zero, (uint)DllImportSearchPath.System32);
            if (winRTErrorModule != IntPtr.Zero)
            {
                getRestrictedErrorInfo = Platform.GetProcAddress<GetRestrictedErrorInfo>(winRTErrorModule);
                setRestrictedErrorInfo = Platform.GetProcAddress<SetRestrictedErrorInfo>(winRTErrorModule);
                roOriginateLanguageException = Platform.GetProcAddress<RoOriginateLanguageException>(winRTErrorModule);
                roReportUnhandledError = Platform.GetProcAddress<RoReportUnhandledError>(winRTErrorModule);
            }
            else
            {
                winRTErrorModule = Platform.LoadLibraryExW("api-ms-win-core-winrt-error-l1-1-0.dll", IntPtr.Zero, (uint)DllImportSearchPath.System32);
                if (winRTErrorModule != IntPtr.Zero)
                {
                    getRestrictedErrorInfo = Platform.GetProcAddress<GetRestrictedErrorInfo>(winRTErrorModule);
                    setRestrictedErrorInfo = Platform.GetProcAddress<SetRestrictedErrorInfo>(winRTErrorModule);
                }
            }
        }
 
        public static void ThrowExceptionForHR(int hr)
        {
            Exception ex = GetExceptionForHR(hr, useGlobalErrorState: true, out bool restoredExceptionFromGlobalState);
            if (restoredExceptionFromGlobalState)
            {
                ExceptionDispatchInfo.Capture(ex).Throw();
            }
            else if (ex is object)
            {
                throw ex;
            }
        }
 
        public static Exception GetExceptionForHR(int hr) => GetExceptionForHR(hr, false, out _);
 
        private static Exception GetExceptionForHR(int hr, bool useGlobalErrorState, out bool restoredExceptionFromGlobalState)
        {
            restoredExceptionFromGlobalState = false;
            if (hr >= 0)
            {
                return null;
            }
 
            ObjectReference<ABI.WinRT.Interop.IErrorInfo.Vftbl> iErrorInfo = null;
            IObjectReference restrictedErrorInfoToSave = null;
            Exception ex;
            string description = null;
            string restrictedError = null;
            string restrictedErrorReference = null;
            string restrictedCapabilitySid = null;
            bool hasOtherLanguageException = false;
 
            if (useGlobalErrorState && getRestrictedErrorInfo != null)
            {
                Marshal.ThrowExceptionForHR(getRestrictedErrorInfo(out IntPtr restrictedErrorInfoPtr));
 
                if (restrictedErrorInfoPtr != IntPtr.Zero)
                {
                    IObjectReference restrictedErrorInfoRef = ObjectReference<ABI.WinRT.Interop.IRestrictedErrorInfo.Vftbl>.Attach(ref restrictedErrorInfoPtr);
                    restrictedErrorInfoToSave = restrictedErrorInfoRef.As<ABI.WinRT.Interop.IRestrictedErrorInfo.Vftbl>();
 
                    ABI.WinRT.Interop.IRestrictedErrorInfo restrictedErrorInfo = new ABI.WinRT.Interop.IRestrictedErrorInfo(restrictedErrorInfoRef);
                    restrictedErrorInfo.GetErrorDetails(out description, out int hrLocal, out restrictedError, out restrictedCapabilitySid);
                    restrictedErrorReference = restrictedErrorInfo.GetReference();
                    if (restrictedErrorInfoRef.TryAs<ABI.WinRT.Interop.ILanguageExceptionErrorInfo.Vftbl>(out var languageErrorInfoRef) >= 0)
                    {
                        ILanguageExceptionErrorInfo languageErrorInfo = new ABI.WinRT.Interop.ILanguageExceptionErrorInfo(languageErrorInfoRef);
                        using IObjectReference languageException = languageErrorInfo.GetLanguageException();
                        if (languageException is object)
                        {
                            if (languageException.IsReferenceToManagedObject)
                            {
                                ex = ComWrappersSupport.FindObject<Exception>(languageException.ThisPtr);
                                if (GetHRForException(ex) == hr)
                                {
                                    restoredExceptionFromGlobalState = true;
                                    return ex;
                                }
                            }
                            else
                            {
                                hasOtherLanguageException = true;
                            }
                        }
                    }
                    else
                    {
                        if (hr == hrLocal)
                        {
                            restrictedErrorInfoRef.TryAs<ABI.WinRT.Interop.IErrorInfo.Vftbl>(out iErrorInfo);
                        }
                    }
                }
            }
 
            using (iErrorInfo)
            {
                switch (hr)
                {
                    case E_ILLEGAL_STATE_CHANGE:
                    case E_ILLEGAL_METHOD_CALL:
                    case E_ILLEGAL_DELEGATE_ASSIGNMENT:
                    case APPMODEL_ERROR_NO_PACKAGE:
                        ex = new InvalidOperationException(description);
                        break;
                    default:
                        ex = Marshal.GetExceptionForHR(hr, iErrorInfo?.ThisPtr ?? (IntPtr)(-1));
                        break;
                }
            }
 
            ex.AddExceptionDataForRestrictedErrorInfo(
                description,
                restrictedError,
                restrictedErrorReference,
                restrictedCapabilitySid,
                restrictedErrorInfoToSave,
                hasOtherLanguageException);
 
            return ex;
        }
 
        public static unsafe void SetErrorInfo(Exception ex)
        {
            if (getRestrictedErrorInfo != null && setRestrictedErrorInfo != null && roOriginateLanguageException != null)
            {
                // If the exception has information for an IRestrictedErrorInfo, use that
                // as our error so as to propagate the error through WinRT end-to-end.
                if (ex.TryGetRestrictedLanguageErrorObject(out var restrictedErrorObject))
                {
                    using (restrictedErrorObject)
                    {
                        setRestrictedErrorInfo(restrictedErrorObject.ThisPtr);
                    }
                }
                else
                {
                    string message = ex.Message;
                    if (string.IsNullOrEmpty(message))
                    {
                        message = ex.GetType().FullName;
                    }
 
                    IntPtr hstring;
 
                    if (Platform.WindowsCreateString(message, message.Length, &hstring) != 0)
                    {
                        hstring = IntPtr.Zero;
                    }
 
                    using var managedExceptionWrapper = ComWrappersSupport.CreateCCWForObject(ex);
                    roOriginateLanguageException(GetHRForException(ex), hstring, managedExceptionWrapper.ThisPtr);
                }
            }
            else
            {
                using var iErrorInfo = ComWrappersSupport.CreateCCWForObject(new ManagedExceptionErrorInfo(ex));
                SetErrorInfo(0, iErrorInfo.ThisPtr);
            }
        }
 
        public static void ReportUnhandledError(Exception ex)
        {
            SetErrorInfo(ex);
            if (getRestrictedErrorInfo != null && roReportUnhandledError != null)
            {
                Marshal.ThrowExceptionForHR(getRestrictedErrorInfo(out IntPtr ppRestrictedErrorInfo));
                using var restrictedErrorRef = ObjectReference<IUnknownVftbl>.Attach(ref ppRestrictedErrorInfo);
                roReportUnhandledError(restrictedErrorRef.ThisPtr);
            }
        }
 
        public static int GetHRForException(Exception ex)
        {
            int hr = ex.HResult;
            if (ex.TryGetRestrictedLanguageErrorObject(out var restrictedErrorObject))
            {
                restrictedErrorObject.AsType<ABI.WinRT.Interop.IRestrictedErrorInfo>().GetErrorDetails(out _, out hr, out _, out _);
            }
            if (hr == COR_E_OBJECTDISPOSED)
            {
                return RO_E_CLOSED;
            }
            return hr;
        }
 
        //
        // Exception requires anything to be added into Data dictionary is serializable
        // This wrapper is made serializable to satisfy this requirement but does NOT serialize
        // the object and simply ignores it during serialization, because we only need
        // the exception instance in the app to hold the error object alive.
        //
        [Serializable]
        internal class __RestrictedErrorObject
        {
            // Hold the error object instance but don't serialize/deserialize it
            [NonSerialized]
            private readonly IObjectReference _realErrorObject;
 
            internal __RestrictedErrorObject(IObjectReference errorObject)
            {
                _realErrorObject = errorObject;
            }
 
            public IObjectReference RealErrorObject
            {
                get
                {
                    return _realErrorObject;
                }
            }
        }
 
        internal static void AddExceptionDataForRestrictedErrorInfo(
            this Exception ex,
            string description,
            string restrictedError,
            string restrictedErrorReference,
            string restrictedCapabilitySid,
            IObjectReference restrictedErrorObject,
            bool hasRestrictedLanguageErrorObject = false)
        {
            IDictionary dict = ex.Data;
            if (dict != null)
            {
                dict.Add("Description", description);
                dict.Add("RestrictedDescription", restrictedError);
                dict.Add("RestrictedErrorReference", restrictedErrorReference);
                dict.Add("RestrictedCapabilitySid", restrictedCapabilitySid);
 
                // Keep the error object alive so that user could retrieve error information
                // using Data["RestrictedErrorReference"]
                dict.Add("__RestrictedErrorObjectReference", restrictedErrorObject == null ? null : new __RestrictedErrorObject(restrictedErrorObject));
                dict.Add("__HasRestrictedLanguageErrorObject", hasRestrictedLanguageErrorObject);
            }
        }
 
        internal static bool TryGetRestrictedLanguageErrorObject(
            this Exception ex,
            out IObjectReference restrictedErrorObject)
        {
            restrictedErrorObject = null;
            IDictionary dict = ex.Data;
            if (dict != null && dict.Contains("__HasRestrictedLanguageErrorObject"))
            {
                if (dict.Contains("__RestrictedErrorObjectReference"))
                {
                    if (dict["__RestrictedErrorObjectReference"] is __RestrictedErrorObject restrictedObject)
                        restrictedErrorObject = restrictedObject.RealErrorObject;
                }
                return (bool)dict["__HasRestrictedLanguageErrorObject"]!;
            }
 
            return false;
        }
 
        public static Exception AttachRestrictedErrorInfo(Exception e)
        {
            // If there is no exception, then the restricted error info doesn't apply to it
            if (e != null)
            {
                try
                {
                    // Get the restricted error info for this thread and see if it may correlate to the current
                    // exception object.  Note that in general the thread's IRestrictedErrorInfo is not meant for
                    // exceptions that are marshaled Windows.Foundation.HResults and instead are intended for
                    // HRESULT ABI return values.   However, in many cases async APIs will set the thread's restricted
                    // error info as a convention in order to provide extended debugging information for the ErrorCode
                    // property.
                    Marshal.ThrowExceptionForHR(getRestrictedErrorInfo(out IntPtr restrictedErrorInfoPtr));
 
                    if (restrictedErrorInfoPtr != IntPtr.Zero)
                    {
                        IObjectReference restrictedErrorInfoRef = ObjectReference<ABI.WinRT.Interop.IRestrictedErrorInfo.Vftbl>.Attach(ref restrictedErrorInfoPtr);
 
                        ABI.WinRT.Interop.IRestrictedErrorInfo restrictedErrorInfo = new ABI.WinRT.Interop.IRestrictedErrorInfo(restrictedErrorInfoRef);
 
                        restrictedErrorInfo.GetErrorDetails(out string description,
                                                            out int restrictedErrorInfoHResult,
                                                            out string restrictedDescription,
                                                            out string capabilitySid);
 
                        // Since this is a special case where by convention there may be a correlation, there is not a
                        // guarantee that the restricted error info does belong to the async error code.  In order to
                        // reduce the risk that we associate incorrect information with the exception object, we need
                        // to apply a heuristic where we attempt to match the current exception's HRESULT with the
                        // HRESULT the IRestrictedErrorInfo belongs to.  If it is a match we will assume association
                        // for the IAsyncInfo case.
                        if (e.HResult == restrictedErrorInfoHResult)
                        {
                            e.AddExceptionDataForRestrictedErrorInfo(description,
                                                                    restrictedDescription,
                                                                     restrictedErrorInfo.GetReference(),
                                                                     capabilitySid,
                                                                     restrictedErrorInfoRef.As<ABI.WinRT.Interop.IRestrictedErrorInfo.Vftbl>());
                        }
                    }
                }
                catch
                {
                    // If we can't get the restricted error info, then proceed as if it isn't associated with this
                    // error.
                }
            }
 
            return e;
        }
    }
 
    internal static class ExceptionExtensions
    {
        public static void SetHResult(this Exception ex, int value)
        {
            ex.GetType().GetProperty("HResult").SetValue(ex, value);
        }
 
        internal static Exception GetExceptionForHR(this Exception innerException, int hresult, string messageResource)
        {
            Exception e;
            if (innerException != null)
            {
                string message = innerException.Message ?? messageResource;
                e = new Exception(message, innerException);
            }
            else
            {
                e = new Exception(messageResource);
            }
            e.SetHResult(hresult);
            return e;
        }
    }
 
    internal class ErrorStrings
    {
        internal static string Format(string format, params object[] args) => String.Format(format, args);
 
        internal static readonly string Arg_IndexOutOfRangeException = "Index was outside the bounds of the array.";
        internal static readonly string Arg_KeyNotFound = "The given key was not present in the dictionary.";
        internal static readonly string Arg_KeyNotFoundWithKey = "The given key '{0}' was not present in the dictionary.";
        internal static readonly string Arg_RankMultiDimNotSupported = "Only single dimensional arrays are supported for the requested action.";
        internal static readonly string Argument_AddingDuplicate = "An item with the same key has already been added.";
        internal static readonly string Argument_AddingDuplicateWithKey = "An item with the same key has already been added. Key: {0}";
        internal static readonly string Argument_IndexOutOfArrayBounds = "The specified index is out of bounds of the specified array.";
        internal static readonly string Argument_InsufficientSpaceToCopyCollection = "The specified space is not sufficient to copy the elements from this Collection.";
        internal static readonly string ArgumentOutOfRange_Index = "Index was out of range. Must be non-negative and less than the size of the collection.";
        internal static readonly string ArgumentOutOfRange_IndexLargerThanMaxValue = "This collection cannot work with indices larger than Int32.MaxValue - 1 (0x7FFFFFFF - 1).";
        internal static readonly string InvalidOperation_CannotRemoveLastFromEmptyCollection = "Cannot remove the last element from an empty collection.";
        internal static readonly string InvalidOperation_CollectionBackingDictionaryTooLarge = "The collection backing this Dictionary contains too many elements.";
        internal static readonly string InvalidOperation_CollectionBackingListTooLarge = "The collection backing this List contains too many elements.";
        internal static readonly string InvalidOperation_EnumEnded = "Enumeration already finished.";
        internal static readonly string InvalidOperation_EnumFailedVersion = "Collection was modified; enumeration operation may not execute.";
        internal static readonly string InvalidOperation_EnumNotStarted = "Enumeration has not started. Call MoveNext.";
        internal static readonly string NotSupported_KeyCollectionSet = "Mutating a key collection derived from a dictionary is not allowed.";
        internal static readonly string NotSupported_ValueCollectionSet = "Mutating a value collection derived from a dictionary is not allowed.";
    }
}