AggregateGraphMetricsAsync
        (
            String sourceFolderPath,
            Microsoft.Office.Interop.Excel.Workbook workbook
        )
        {
            Debug.Assert(!String.IsNullOrEmpty(sourceFolderPath));
            Debug.Assert(Directory.Exists(sourceFolderPath));
            Debug.Assert(workbook != null);
            AssertValid();

            if (this.IsBusy)
            {
                throw new InvalidOperationException(
                          "GraphMetricsAggregator.AggregateGraphMetricsAsync: An"
                          + " asynchronous operation is already in progress."
                          );
            }

            // Wrap the arguments in an object that can be passed to
            // BackgroundWorker.RunWorkerAsync().

            AggregateGraphMetricsAsyncArgs oAggregateGraphMetricsAsyncArgs =
                new AggregateGraphMetricsAsyncArgs();

            oAggregateGraphMetricsAsyncArgs.SourceFolderPath = sourceFolderPath;
            oAggregateGraphMetricsAsyncArgs.Workbook         = workbook;

            oAggregateGraphMetricsAsyncArgs.WorkbookSettings =
                GetWorkbookSettings(workbook);

            // Create a BackgroundWorker and handle its events.

            m_oBackgroundWorker = new BackgroundWorker();
            m_oBackgroundWorker.WorkerReportsProgress      = true;
            m_oBackgroundWorker.WorkerSupportsCancellation = true;

            m_oBackgroundWorker.DoWork += new DoWorkEventHandler(
                BackgroundWorker_DoWork);

            m_oBackgroundWorker.ProgressChanged +=
                new ProgressChangedEventHandler(BackgroundWorker_ProgressChanged);

            m_oBackgroundWorker.RunWorkerCompleted +=
                new RunWorkerCompletedEventHandler(
                    BackgroundWorker_RunWorkerCompleted);

            m_oBackgroundWorker.RunWorkerAsync(oAggregateGraphMetricsAsyncArgs);
        }
        BackgroundWorker_DoWork
        (
            object sender,
            DoWorkEventArgs e
        )
        {
            AssertValid();

            Debug.Assert(e.Argument is AggregateGraphMetricsAsyncArgs);

            AggregateGraphMetricsAsyncArgs oAggregateGraphMetricsAsyncArgs =
                (AggregateGraphMetricsAsyncArgs)e.Argument;

            AggregateGraphMetricsInternal(oAggregateGraphMetricsAsyncArgs,
                                          m_oBackgroundWorker, e);
        }
    AggregateGraphMetricsAsync
    (
        String sourceFolderPath,
        Microsoft.Office.Interop.Excel.Workbook workbook
    )
    {
        Debug.Assert( !String.IsNullOrEmpty(sourceFolderPath) );
        Debug.Assert( Directory.Exists(sourceFolderPath) );
        Debug.Assert(workbook != null);
        AssertValid();

        if (this.IsBusy)
        {
            throw new InvalidOperationException(
                "GraphMetricsAggregator.AggregateGraphMetricsAsync: An"
                + " asynchronous operation is already in progress."
                );
        }

        // Wrap the arguments in an object that can be passed to
        // BackgroundWorker.RunWorkerAsync().

        AggregateGraphMetricsAsyncArgs oAggregateGraphMetricsAsyncArgs =
            new AggregateGraphMetricsAsyncArgs();

        oAggregateGraphMetricsAsyncArgs.SourceFolderPath = sourceFolderPath;
        oAggregateGraphMetricsAsyncArgs.Workbook = workbook;

        oAggregateGraphMetricsAsyncArgs.WorkbookSettings =
            GetWorkbookSettings(workbook);

        // Create a BackgroundWorker and handle its events.

        m_oBackgroundWorker = new BackgroundWorker();
        m_oBackgroundWorker.WorkerReportsProgress = true;
        m_oBackgroundWorker.WorkerSupportsCancellation = true;

        m_oBackgroundWorker.DoWork += new DoWorkEventHandler(
            BackgroundWorker_DoWork);

        m_oBackgroundWorker.ProgressChanged +=
            new ProgressChangedEventHandler(BackgroundWorker_ProgressChanged);

        m_oBackgroundWorker.RunWorkerCompleted +=
            new RunWorkerCompletedEventHandler(
                BackgroundWorker_RunWorkerCompleted);

        m_oBackgroundWorker.RunWorkerAsync(oAggregateGraphMetricsAsyncArgs);
    }
    AggregateGraphMetricsInternal
    (
        AggregateGraphMetricsAsyncArgs oAggregateGraphMetricsAsyncArgs,
        BackgroundWorker oBackgroundWorker,
        DoWorkEventArgs oDoWorkEventArgs
    )
    {
        Debug.Assert(oAggregateGraphMetricsAsyncArgs != null);
        Debug.Assert(oBackgroundWorker != null);
        Debug.Assert(oDoWorkEventArgs != null);
        AssertValid();

        List<OverallMetricsInfo> oOverallMetricsInfos =
            new List<OverallMetricsInfo>();

        OverallMetricsReader oOverallMetricsReader =
            new OverallMetricsReader();

        foreach ( String sFilePath in Directory.GetFiles(
            oAggregateGraphMetricsAsyncArgs.SourceFolderPath, "*.xlsx") )
        {
            if (oBackgroundWorker.CancellationPending)
            {
                oDoWorkEventArgs.Cancel = true;
                return;
            }

            try
            {
                if (!NodeXLWorkbookUtil.FileIsNodeXLWorkbook(sFilePath))
                {
                    continue;
                }
            }
            catch (IOException)
            {
                // Skip any workbooks that are already open, or that have any
                // other problems that prevent them from being opened.

                continue;
            }

            oBackgroundWorker.ReportProgress(0,
                String.Format(
                    "Reading \"{0}\"."
                    ,
                    Path.GetFileName(sFilePath)
                ) );

            OverallMetricsInfo oOverallMetricsInfo;

            for (Int32 iAttempt = 0; iAttempt < 2; iAttempt++)
            {
                // Have overall metrics already been calculated for the
                // workbook?

                if ( TryGetGraphMetricsForOneNodeXLWorkbook(sFilePath,
                    out oOverallMetricsInfo) )
                {
                    // Yes.

                    oOverallMetricsInfos.Add(oOverallMetricsInfo);
                    break;
                }

                if (iAttempt == 0)
                {
                    // No.  Calculate them.

                    TaskAutomator.AutomateOneWorkbookIndirect(sFilePath,
                        oAggregateGraphMetricsAsyncArgs.WorkbookSettings);
                }
            }
        }

        if (oOverallMetricsInfos.Count > 0)
        {
            WriteOverallMetricsToNewWorkbook(
                oAggregateGraphMetricsAsyncArgs.Workbook.Application,
                oOverallMetricsInfos);
        }

        oBackgroundWorker.ReportProgress(0,
            String.Format(
                "Done.  NodeXL workbooks aggregated: {0}."
                ,
                oOverallMetricsInfos.Count
            ) );
    }
        AggregateGraphMetricsInternal
        (
            AggregateGraphMetricsAsyncArgs oAggregateGraphMetricsAsyncArgs,
            BackgroundWorker oBackgroundWorker,
            DoWorkEventArgs oDoWorkEventArgs
        )
        {
            Debug.Assert(oAggregateGraphMetricsAsyncArgs != null);
            Debug.Assert(oBackgroundWorker != null);
            Debug.Assert(oDoWorkEventArgs != null);
            AssertValid();

            List <OverallMetricsInfo> oOverallMetricsInfos =
                new List <OverallMetricsInfo>();

            OverallMetricsReader oOverallMetricsReader =
                new OverallMetricsReader();

            foreach (String sFilePath in Directory.GetFiles(
                         oAggregateGraphMetricsAsyncArgs.SourceFolderPath, "*.xlsx"))
            {
                if (oBackgroundWorker.CancellationPending)
                {
                    oDoWorkEventArgs.Cancel = true;
                    return;
                }

                try
                {
                    if (!NodeXLWorkbookUtil.FileIsNodeXLWorkbook(sFilePath))
                    {
                        continue;
                    }
                }
                catch (IOException)
                {
                    // Skip any workbooks that are already open, or that have any
                    // other problems that prevent them from being opened.

                    continue;
                }

                oBackgroundWorker.ReportProgress(0,
                                                 String.Format(
                                                     "Reading \"{0}\"."
                                                     ,
                                                     Path.GetFileName(sFilePath)
                                                     ));

                OverallMetricsInfo oOverallMetricsInfo;

                for (Int32 iAttempt = 0; iAttempt < 2; iAttempt++)
                {
                    // Have overall metrics already been calculated for the
                    // workbook?

                    if (TryGetGraphMetricsForOneNodeXLWorkbook(sFilePath,
                                                               out oOverallMetricsInfo))
                    {
                        // Yes.

                        oOverallMetricsInfos.Add(oOverallMetricsInfo);
                        break;
                    }

                    if (iAttempt == 0)
                    {
                        // No.  Calculate them.

                        TaskAutomator.AutomateOneWorkbookIndirect(sFilePath,
                                                                  oAggregateGraphMetricsAsyncArgs.WorkbookSettings);
                    }
                }
            }

            if (oOverallMetricsInfos.Count > 0)
            {
                WriteOverallMetricsToNewWorkbook(
                    oAggregateGraphMetricsAsyncArgs.Workbook.Application,
                    oOverallMetricsInfos);
            }

            oBackgroundWorker.ReportProgress(0,
                                             String.Format(
                                                 "Done.  NodeXL workbooks aggregated: {0}."
                                                 ,
                                                 oOverallMetricsInfos.Count
                                                 ));
        }