在后台处理 运行 时保持控制台应用程序活动和事件驱动

Keeping console app alive and event-driven while processes run in the background

我正在尝试编写一个控制台应用程序,该应用程序在后台由 运行 个进程充当 "job manager"。这些进程将是 运行 个传入参数的 JScript 文件。这个控制台应用程序将分布在许多机器上,并将从集中源(即数据库)中提取以获取作业。此应用程序的目的是消除所有这些机器上对个性化批处理文件的需求。

我无法使应用程序保持活动状态。在我包含的代码中,您可以在我的主函数中看到我正在对 JobManger 的 StartNewJobs() 方法进行初始调用。在初始调用此方法后,我希望我的应用程序成为事件驱动的,仅在进程退出时唤醒并 运行,从而允许我启动新进程。我遇到的问题 运行 是一旦 main() 函数完成(当初始 StartNewJobs() 方法完成时)控制台关闭并且程序结束。

我的问题是 什么是让我的控制台应用程序保持活动状态并允许它由事件驱动而不是程序驱动的正确方法? 我知道我可能需要一段时间(true) 在 main 函数的末尾,但这看起来草率且不正确。

我们正在尝试替换的批处理文件:

C:\Windows\SysWOW64\cscript.exe c:\temp\somejscriptfile.js 49f1bdd8-5e6b-40cc-92bc-eb20c237a959
C:\Windows\SysWOW64\cscript.exe c:\temp\somejscriptfile.js 654e3783-a1b6-43be-8027-c7d060bf131f
...

Program.cs:

using DistributedJobs.Data;
using DistributedJobs.Logging;
using DistributedJobs.Models;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using System;

namespace DistributedJobs
{
    class Program
    {
        static void Main(string[] args)
        {
            //Get intial objects/settings
            ILogger logger = new Logger(Properties.Settings.Default.LoggingLevel, EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>());
            IDataProvider dataProvider = new SQLDataProvider();
            DMSPollingJobType availableJobTypes = DMSPollingJobType.FlatFile;
            if (Properties.Settings.Default.SupportsVPN)
            {
                availableJobTypes |= DMSPollingJobType.VPN;
            }
            String executableLocation = Properties.Settings.Default.ExecutableLocation;
            String jsLocation = Properties.Settings.Default.JSLocation;
            Int32 maxProcesses = Properties.Settings.Default.MaxProcesses;

            //Create job manager and start new processes/jobs
            DMSJobManager jobManager = new DMSJobManager(logger, dataProvider, availableJobTypes, executableLocation, jsLocation, maxProcesses);
            jobManager.StartNewJobs();
        }
    }
}

JobManager.cs:

using DistributedJobs.Models;
using System.Diagnostics;
using System;
using System.Collections.Generic;
using DistributedJobs.Logging;

namespace DistributedJobs.Data
{
    public class JobManager
    {
        private IDataProvider DataProvider;
        private ILogger Logger;
        private Dictionary<Job, Process> RunningProcesses;
        private JobType AvailableJobTypes;
        private String ExecutableLocation;
        private String JSLocation;
        private Int32 MaxProcesses;

        public Boolean CanStartNewJob
        {
            get
            {
                Boolean canStartNewJob = false;
                if (RunningProcesses.Count < MaxProcesses)
                {
                    canStartNewJob = true;
                }
                foreach (KeyValuePair<Job, Process> entry in RunningProcesses)
                {
                    if (entry.Key.JobType != JobType.FlatFile)
                    {
                        canStartNewJob = false;
                        break;
                    }
                }
                return canStartNewJob;
            }
        }

        public JobManager(ILogger logger, IDataProvider dataProvider, JobType availableJobTypes, String executableLocation, String jsLocation, Int32 maxProcesses)
        {
            Logger = logger;
            DataProvider = dataProvider;
            RunningProcesses = new Dictionary<Job, Process>();
            AvailableJobTypes = availableJobTypes;
            ExecutableLocation = executableLocation;
            JSLocation = jsLocation;
            MaxProcesses = maxProcesses;
        }

        public void StartNewJobs()
        {
            while (CanStartNewJob)
            {
                Job newJob = DataProvider.GetNextScheduledJob(AvailableJobTypes);
                if (newJob != null)
                {
                    Process newProcess = CreateNewProcess(newJob);
                    RunningProcesses.Add(newJob, newProcess);
                    newProcess.Start();
                }
            }
        }

        public Process CreateNewProcess(Job job)
        {
            ProcessStartInfo startInfo = new ProcessStartInfo();
            startInfo.FileName = ExecutableLocation;
            startInfo.Arguments = JSLocation + " " + job.JobID.ToString();
            startInfo.UseShellExecute = false;
            Process retProcess = new Process()
            {
                StartInfo = startInfo,
                EnableRaisingEvents = true
            };
            retProcess.Exited += new EventHandler(JobFinished);
            return retProcess;
        }

        public void JobFinished(object sender, EventArgs e)
        {
            Job finishedJob = null;
            foreach (KeyValuePair<Job, Process> entry in RunningProcesses)
            {
                if ((Process)sender == entry.Value)
                {
                    finishedJob = entry.Key;
                    break;
                }
            }
            if (finishedJob != null)
            {
                RunningProcesses.Remove(finishedJob);
                StartNewJobs();
            }
        }
    }
}

您可以尝试使用 Application.Run()(System.Windows.Forms)。这将启动一个标准的消息循环。

所以在你的 Main 方法的末尾添加一个 Application.Run():

    static void Main(string[] args)
    {
        //Get intial objects/settings
        ILogger logger = new Logger(Properties.Settings.Default.LoggingLevel, EnterpriseLibraryContainer.Current.GetInstance<ExceptionManager>());
        IDataProvider dataProvider = new SQLDataProvider();
        DMSPollingJobType availableJobTypes = DMSPollingJobType.FlatFile;
        if (Properties.Settings.Default.SupportsVPN)
        {
            availableJobTypes |= DMSPollingJobType.VPN;
        }
        String executableLocation = Properties.Settings.Default.ExecutableLocation;
        String jsLocation = Properties.Settings.Default.JSLocation;
        Int32 maxProcesses = Properties.Settings.Default.MaxProcesses;

        //Create job manager and start new processes/jobs
        DMSJobManager jobManager = new DMSJobManager(logger, dataProvider, availableJobTypes, executableLocation, jsLocation, maxProcesses);
        jobManager.StartNewJobs();

        // start message loop
        Application.Run();
    }