コード例 #1
0
ファイル: TaskUtil.cs プロジェクト: vonsid/scrobblemapper
        /// <summary>
        /// Performs an asynchronous task in foreground (blocking the host form's user input) and reports its progress.
        /// </summary>
        /// <typeparam name="T">The task's result type</typeparam>
        /// <param name="host">The host form</param>
        /// <param name="taskContext">The task to perform</param>
        /// <param name="onSuccess">What to do if the operation succeceeds; the parameter is the task's result</param>
        /// <param name="onError">What to do if the operation fails; the parameter is the cause of the failure</param>
        public static void PerformForegroundTask <T>(Form host, IReportingTask <T> taskContext, Action <T> onSuccess, Action <Exception> onError)
        {
            // Disable the host form (like ShowDialog would do)
            host.Enabled = false;

            var reporter = new ProgressReporter(taskContext);

            reporter.Show(host);

            // When the task completes,
            taskContext.Task.ContinueWith(delegate
            {
                // Determine if there was an error
                bool success    = false;
                bool canceled   = taskContext.Task.IsCanceled;
                Exception error = null;

                if (!taskContext.Task.IsCanceled)
                {
                    error = taskContext.Task.Exception;

                    // It really sucks to have an aggregate exception with a single inner...
                    while (error != null && error is AggregateException && (error as AggregateException).InnerExceptions.Count == 1)
                    {
                        error = error.InnerException;
                    }

                    // Target invocation exceptions are also meaningless wrappers
                    while (error != null && error is TargetInvocationException)
                    {
                        error = error.InnerException;
                    }

                    success = error == null;
                }

                // Bring back the host context (in its own thread)
                bool enteredOnce            = false;
                Action bringBackHostContext = () =>
                {
                    if (!enteredOnce)
                    {
                        enteredOnce = true;

                        reporter.Close();
                        reporter = null;

                        host.Enabled = true;
                        host.Focus();

                        // Execute the provided actions
                        if (success)
                        {
                            var result = taskContext.Task.Result;
                            taskContext.Dispose();
                            onSuccess(result);
                        }
                        else if (!canceled)
                        {
                            taskContext.Dispose();
                            onError(error);
                        }
                    }
                };

                // Try as many times as required, but only allow 1 execution of the action
                IAsyncResult ar = host.BeginInvoke(bringBackHostContext);
                while (!ar.AsyncWaitHandle.WaitOne(100, false) && !enteredOnce)
                {
                    ar = host.BeginInvoke(bringBackHostContext);
                }
            });
        }