/// <summary>
        /// This function is the callback used to execute the command when the menu item is clicked.
        /// See the constructor to see how the menu item is associated with this function using
        /// OleMenuCommandService service and MenuCommand class.
        /// </summary>
        /// <param name="sender">Event sender.</param>
        /// <param name="e">Event args.</param>
        private void Execute(object sender, EventArgs e)
        {
            ThreadHelper.ThrowIfNotOnUIThread();
            _commandExecutionLimiter.StartCommandExec();
            EnvDTE.DTE           dte        = (EnvDTE.DTE)ThreadHelper.JoinableTaskFactory.Run(() => ServiceProvider.GetServiceAsync(typeof(EnvDTE.DTE)));
            IList <ProjectData>  projects   = (dte.ActiveSolutionProjects as Object[] ?? new Object[0]).OfType <EnvDTE.Project>().Select(project => new ProjectData(project)).ToList();
            ProjectsAnalysisStat statistics = new ProjectsAnalysisStat(String.Join(",", projects.Select(project => project.Name)), projects.Count);

            ThreadHelper.JoinableTaskFactory.RunAsync(() => ExecuteProjectsAnalysisAsync(projects, statistics));
        }
        private async Task ExecuteProjectsAnalysisImplAsync(IList <ProjectData> projects, ProjectsAnalysisStat statistics, String appPath, IConfigDataProvider configDataProvider)
        {
            await OutputHelper.OutputMessageAsync(ServiceProvider, $"Source code analysis for {statistics.TargetType} named \"{statistics.ProjectNames}\" is started");

            foreach (ProjectData project in projects)
            {
                String target = project.FileName;
                if (String.Equals(project.LanguageId, LanguageCSharpId))
                {
                    String          configPath  = ConfigFinder.FindConfig(configDataProvider, target);
                    OutputLevel     outputLevel = configDataProvider.GetOutputLevel();
                    ExecutionResult result      = await ExecutionHelper.ExecuteSourceCodeAnalysisAsync(appPath, target, configPath, outputLevel);

                    if (result.ExitCode == 0)
                    {
                        ++statistics.SuccessCount;
                    }
                    else
                    {
                        ++statistics.FailedCount;
                    }
                    await OutputHelper.OutputTargetAnalysisResultAsync(ServiceProvider, result, target, "project");
                }
                else
                {
                    ++statistics.SkippedCount;
                    await OutputHelper.OutputMessageAsync(ServiceProvider, $"Project \"{target}\" can't be processed due to unsupported language");
                }
            }
            await OutputHelper.OutputMessageAsync(ServiceProvider, $"Source code analysis for {statistics.TargetType} named \"{statistics.ProjectNames}\" is finished");

            await UIHelper.ShowProjectsSummaryAsync(_package, statistics);
        }
        private async Task ExecuteProjectsAnalysisAsync(IList <ProjectData> projects, ProjectsAnalysisStat statistics)
        {
            IConfigDataProvider configDataProvider = new AppDataConfigDataProvider(ConfigDefs.ConfigDirectory, ConfigDefs.ConfigFilename);
            String appPath = await SourceCodeAnalysisAppHelper.PrepareAnalysisAppAsync(_package, configDataProvider.GetAppPath());

            if (!String.IsNullOrEmpty(appPath))
            {
                await ExecuteProjectsAnalysisImplAsync(projects, statistics, appPath, configDataProvider);
            }
            await _commandExecutionLimiter.StopCommandExecAsync();
        }