internal static Exception AttachRestrictedErrorInfo(Exception e) { // If there is no exception, then the restricted error info doesn't apply to it if (e != null) { System.IntPtr pRestrictedErrorInfo = IntPtr.Zero; 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. if (TryGetRestrictedErrorInfo(out pRestrictedErrorInfo)) { string description; string restrictedDescription; string capabilitySid; int restrictedErrorInfoHResult; if (RestrictedErrorInfoHelper.GetErrorDetails( pRestrictedErrorInfo, out description, out restrictedErrorInfoHResult, out restrictedDescription, out capabilitySid) && (e.HResult == restrictedErrorInfoHResult)) { // 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. string errorReference; RestrictedErrorInfoHelper.GetReference(pRestrictedErrorInfo, out errorReference); object restrictedErrorInfo = McgMarshal.ComInterfaceToObject(pRestrictedErrorInfo, InternalTypes.IRestrictedErrorInfo); InteropExtensions.AddExceptionDataForRestrictedErrorInfo( e, restrictedDescription, errorReference, capabilitySid, restrictedErrorInfo); } } } catch (Exception) { // If we can't get the restricted error info, then proceed as if it isn't associated with this // error. } finally { McgMarshal.ComSafeRelease(pRestrictedErrorInfo); } } return(e); }
/// <summary>Gets a Task to represent the asynchronous operation.</summary> /// <param name="source">The asynchronous operation.</param> /// <param name="cancellationToken">The token used to request cancellation of the asynchronous operation.</param> /// <returns>The Task representing the asynchronous operation.</returns> public static Task <TResult> AsTask <TResult>(this IAsyncOperation <TResult> source, CancellationToken cancellationToken) { if (source == null) { throw new ArgumentNullException("source"); } Contract.EndContractBlock(); // If source is actually a NetFx-to-WinRT adapter, unwrap it instead of creating a new Task: var wrapper = source as TaskToAsyncOperationAdapter <TResult>; if (wrapper != null && !wrapper.CompletedSynchronously) { Task <TResult> innerTask = wrapper.Task as Task <TResult>; Debug.Assert(innerTask != null); Debug.Assert(innerTask.Status != TaskStatus.Created); // Is WaitingForActivation a legal state at this moment? if (!innerTask.IsCompleted) { // The race here is benign: If the task completes here, the concatination is useless, but not damaging. if (cancellationToken.CanBeCanceled && wrapper.CancelTokenSource != null) { ConcatenateCancelTokens(cancellationToken, wrapper.CancelTokenSource, innerTask); } } return(innerTask); } // Fast path to return a completed Task if the operation has already completed switch (source.Status) { case AsyncStatus.Completed: return(Task.FromResult(source.GetResults())); case AsyncStatus.Error: return(Task.FromException <TResult>(RestrictedErrorInfoHelper.AttachRestrictedErrorInfo(source.ErrorCode))); case AsyncStatus.Canceled: return(Task.FromCancellation <TResult>(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true))); } // Benign race: source may complete here. Things still work, just not taking the fast path. // Source is not a NetFx-to-WinRT adapter, but a native future. Hook up the task: var bridge = new AsyncInfoToTaskBridge <TResult, VoidValueTypeParameter>(cancellationToken); source.Completed = new AsyncOperationCompletedHandler <TResult>(bridge.CompleteFromAsyncOperation); bridge.RegisterForCancellation(source); return(bridge.Task); }
/// <summary>Completes the task from the completed asynchronous operation.</summary> /// <param name="asyncInfo">The asynchronous operation.</param> /// <param name="getResultsFunction">A function used to retrieve the TResult from the async operation; may be null.</param> /// <param name="asyncStatus">The status of the asynchronous operation.</param> private void Complete(IAsyncInfo asyncInfo, Func <IAsyncInfo, TResult> getResultsFunction, AsyncStatus asyncStatus) { if (asyncInfo == null) { throw new ArgumentNullException(nameof(asyncInfo)); } Contract.EndContractBlock(); if (System.Threading.Tasks.Task.s_asyncDebuggingEnabled) { System.Threading.Tasks.Task.RemoveFromActiveTasks(base.Task.Id); } try { Debug.Assert(asyncInfo.Status == asyncStatus, "asyncInfo.Status does not match asyncStatus; are we dealing with a faulty IAsyncInfo implementation?"); // Assuming a correct underlying implementation, the task should not have been // completed yet. If it is completed, we shouldn't try to do any further work // with the operation or the task, as something is horked. bool taskAlreadyCompleted = Task.IsCompleted; Debug.Assert(!taskAlreadyCompleted, "Expected the task to not yet be completed."); if (taskAlreadyCompleted) { throw new InvalidOperationException(SR.InvalidOperation_InvalidAsyncCompletion); } // Clean up our registration with the cancellation token, noting that we're now in the process of cleaning up. CancellationTokenRegistration ctr; lock (StateLock) { _completing = true; ctr = _ctr; // under lock to avoid torn reads _ctr = default(CancellationTokenRegistration); } ctr.Unregister(); // It's ok if we end up unregistering a not-initialized registration; it'll just be a nop. try { // Find out how the async operation completed. It must be in a terminal state. bool terminalState = asyncStatus == AsyncStatus.Completed || asyncStatus == AsyncStatus.Canceled || asyncStatus == AsyncStatus.Error; Debug.Assert(terminalState, "The async operation should be in a terminal state."); if (!terminalState) { throw new InvalidOperationException(SR.InvalidOperation_InvalidAsyncCompletion); } // Retrieve the completion data from the IAsyncInfo. TResult result = default(TResult); Exception error = null; if (asyncStatus == AsyncStatus.Error) { error = asyncInfo.ErrorCode; // Defend against a faulty IAsyncInfo implementation: if (error == null) { Debug.Assert(false, "IAsyncInfo.Status == Error, but ErrorCode returns a null Exception (implying S_OK)."); error = new InvalidOperationException(SR.InvalidOperation_InvalidAsyncCompletion); } else { error = RestrictedErrorInfoHelper.AttachRestrictedErrorInfo(asyncInfo.ErrorCode); } } else if (asyncStatus == AsyncStatus.Completed && getResultsFunction != null) { try { result = getResultsFunction(asyncInfo); } catch (Exception resultsEx) { // According to the WinRT team, this can happen in some egde cases, such as marshalling errors in GetResults. error = resultsEx; asyncStatus = AsyncStatus.Error; } } // Nothing to retrieve for a canceled operation or for a completed operation with no result. // Complete the task based on the previously retrieved results: bool success = false; switch (asyncStatus) { case AsyncStatus.Completed: if (AsyncCausalityTracer.LoggingOn) { AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, base.Task.Id, AsyncCausalityStatus.Completed); } success = base.TrySetResult(result); break; case AsyncStatus.Error: Debug.Assert(error != null, "The error should have been retrieved previously."); success = base.TrySetException(error); break; case AsyncStatus.Canceled: success = base.TrySetCanceled(_ct.IsCancellationRequested ? _ct : new CancellationToken(true)); break; } Debug.Assert(success, "Expected the outcome to be successfully transfered to the task."); } catch (Exception exc) { // This really shouldn't happen, but could in a variety of misuse cases // such as a faulty underlying IAsyncInfo implementation. Debug.Assert(false, string.Format("Unexpected exception in Complete: {0}", exc.ToString())); if (AsyncCausalityTracer.LoggingOn) { AsyncCausalityTracer.TraceOperationCompletion(CausalityTraceLevel.Required, base.Task.Id, AsyncCausalityStatus.Error); } // For these cases, store the exception into the task so that it makes its way // back to the caller. Only if something went horribly wrong and we can't store the exception // do we allow it to be propagated out to the invoker of the Completed handler. if (!base.TrySetException(exc)) { Debug.Assert(false, "The task was already completed and thus the exception couldn't be stored."); throw; } } } finally { // We may be called on an STA thread which we don't own, so make sure that the RCW is released right // away. Otherwise, if we leave it up to the finalizer, the apartment may already be gone. if (Marshal.IsComObject(asyncInfo)) { Marshal.ReleaseComObject(asyncInfo); } } } // private void Complete(..)
private void ThrowWithIOExceptionDispatchInfo(Exception e) { WinRtIOHelper.NativeExceptionToIOExceptionInfo(RestrictedErrorInfoHelper.AttachRestrictedErrorInfo(_completedOperation.ErrorCode)).Throw(); }
/// <summary> /// This method returns a new Exception object given the HR value. /// /// 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. /// This is done using GetLanguageException API on ILanguageExceptionErrorInfo from IRestrictedErrorInfo. Since ILanguageExceptionErrorInfo is available only on Windows Blue /// 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. /// For Win8 in .NET Native we simply create the exception using the RestrictedErrorInfo and hence only able to give the exception with restrictedErrorMsg. /// 2. In case we do not have the languageException we simply check RestrictedErrorInfo for errorMsg and create an exception using /// <errorMsg>\r\n<restrictedErrorMsg>. This is done for only windows blue. To be backward compatible we only use errorMsg for creating exception in win8. /// 3. PS - This class puts all the logic in try, catch block to ensure that none of the exception helpers /// throw exception themselves. /// </summary> /// <param name="hr"></param> /// <param name="isWinRTScenario"></param> internal static Exception GetExceptionForHRInternalNoThrow(int hr, bool isWinRTScenario, bool isClassicCOM) { Exception ex; IntPtr pRestrictedErrorInfo = IntPtr.Zero; try { if (TryGetRestrictedErrorInfo(out pRestrictedErrorInfo)) { // This is to check whether we need to give post win8 behavior or not. if (isWinRTScenario) { // Check whether the given IRestrictedErrorInfo object supports ILanguageExceptionErrorInfo IntPtr pLanguageExceptionErrorInfo = McgMarshal.ComQueryInterfaceNoThrow(pRestrictedErrorInfo, ref Interop.COM.IID_ILanguageExceptionErrorInfo); if (pLanguageExceptionErrorInfo != IntPtr.Zero) { // We have an LanguageExceptionErrorInfo. IntPtr pUnk; __com_ILanguageExceptionErrorInfo *pComLanguageExceptionErrorInfo = (__com_ILanguageExceptionErrorInfo *)pLanguageExceptionErrorInfo; int result = CalliIntrinsics.StdCall <int>(pComLanguageExceptionErrorInfo->pVtable->pfnGetLanguageException, pLanguageExceptionErrorInfo, out pUnk); McgMarshal.ComSafeRelease(pLanguageExceptionErrorInfo); if (result >= 0 && pUnk != IntPtr.Zero) { try { // Check whether the given pUnk is a managed exception. ComCallableObject ccw; if (ComCallableObject.TryGetCCW(pUnk, out ccw)) { return(ccw.TargetObject as Exception); } } finally { McgMarshal.ComSafeRelease(pUnk); } } } } String message = null, errorInfoReference = null; string errMsg, errCapSid, resErrMsg; int errHr; object restrictedErrorInfo = null; bool hasErrorInfo = false; if (RestrictedErrorInfoHelper.GetErrorDetails(pRestrictedErrorInfo, out errMsg, out errHr, out resErrMsg, out errCapSid) && errHr == hr) { // RestrictedErrorInfo details can be used since the pRestrictedErrorInfo has the same hr value as the hr returned by the native code. // We are in windows blue or above and hence the exceptionMsg is errMsg + "\r\n" + resErrMsg message = String.IsNullOrEmpty(resErrMsg) ? errMsg : errMsg + "\r\n" + resErrMsg; RestrictedErrorInfoHelper.GetReference(pRestrictedErrorInfo, out errorInfoReference); restrictedErrorInfo = McgMarshal.ComInterfaceToObject(pRestrictedErrorInfo, InternalTypes.IRestrictedErrorInfo); hasErrorInfo = true; } if (hr == Interop.COM.RO_E_CLOSED && isWinRTScenario) { hr = Interop.COM.COR_E_OBJECTDISPOSED; } // Now we simply need to set the description and the resDescription by adding an internal method. ex = GetMappingExceptionForHR(hr, message, isClassicCOM, hasErrorInfo); if (restrictedErrorInfo != null) { InteropExtensions.AddExceptionDataForRestrictedErrorInfo(ex, resErrMsg, errorInfoReference, errCapSid, restrictedErrorInfo); } return(ex); } } catch (Exception) { // We can't do any thing here and hence we swallow the exception and get the corresponding hr. } finally { McgMarshal.ComSafeRelease(pRestrictedErrorInfo); } // We could not find any restrictedErrorInfo associated with this object and hence we simply use the hr to create the exception. return(GetMappingExceptionForHR(hr, null, isClassicCOM, hasErrorInfo: false)); }