.Net Core 在Windows上后台运行Console控制台程序
2019-01-18
张子阳
分类: .Net Core
有时候,我们需要在后台运行程序。通常,在Linux上,可以做成系统服务,使用systemctl命令来管理程序的启动和运行;也可以使用 nohup [应用程序] & 来后台运行。在Windows上,想要后台执行程序,通常只能做成Windows服务。有时候,做成Windows服务比较麻烦,当使用.Net Core时,需要将.Net Core程序托管在Windows服务中。但是,可以使用一个间接的方法,让.Net Core控制台程序后台运行。
首先新建一个.Net Core控制台程序,这个程序管它叫AppStarter,它的主要作用就是启动其他程序,然后在启动时通过配置 CreateNoWindow 和 UseShellExecute 来设置实际要启动的控制台程序的启动方式。
在这个例子中,我们的启动器将要启动的程序叫做DataSync.dll,是一个用来做数据同步的小程序,接收的参数为要同步的数据库名称。因此,可以像下面这样,编写一个配置文件appsettings.json:
{
"assembly": "/Users/zhangzy/code/dotnet/MSSQL数据库同步/DataSync/pub/DataSync.dll",
"databases": ["ShopUser", "ShopLog", "ShopCenter"],
"CreateNoWindow": true,
"UseShellExecute": false
}
- assembly: 要启动的程序位置
- databases: 启动程序的参数。上面有3个参数:ShopUser、ShopLog、ShopCenter,实际是启动3个DataSync程序,并分别传入这3个参数。
接下来,编写Program.cs:
using System;
using System.Diagnostics;
using System.IO;
using Microsoft.Extensions.Configuration;
using System.Linq;
namespace DataSyncStarter
{
class Program
{
static IConfigurationRoot config;
static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger ();
static void Main(string[] args)
{
string env = Environment.GetEnvironmentVariable ("ASPNETCORE_ENVIRONMENT");
var builder = new ConfigurationBuilder ()
.SetBasePath (Path.Combine (AppContext.BaseDirectory))
.AddJsonFile ("appsettings.json", false)
.AddJsonFile ($"appsettings.{ env }.json", optional : true);
config = builder.Build ();
SetLogConfig();
var databases = config.GetSection("databases").GetChildren().Select(x=> x.Value).ToArray();
var assembly = config["assembly"];
logger.Info($"控制台启动器运行, 环境:{ env }, 同步数据库:{ string.Join(", ", databases) }, CreateNoWindow:{Convert.ToBoolean(config["CreateNoWindow"])}, UseShellExecute:{Convert.ToBoolean(config["UseShellExecute"])}");
foreach(string db in databases) {
int pid = StartProcess(assembly, db);
var fi = new FileInfo(assembly);
logger.Info($"开启应用: {fi.Name} {db},进程id:{ pid }");
}
}
// 开启进程
static int StartProcess (string assembly, string db) {
var proc = new Process ();
proc.StartInfo.FileName = "dotnet";
proc.StartInfo.Arguments = assembly + " " + db;
proc.StartInfo.CreateNoWindow = Convert.ToBoolean(config["CreateNoWindow"]);
proc.StartInfo.UseShellExecute = Convert.ToBoolean(config["UseShellExecute"]);
proc.Start();
return proc.Id;
}
static void SetLogConfig(){
var nlogConf = new NLog.Config.LoggingConfiguration();
string layout = "${date:format=HH\\:mm\\:ss}|${level}|${message} ${exception}";
var logfile = new NLog.Targets.FileTarget("logfile"){
Layout = layout,
FileName = "${basedir}/logs/${shortdate}.log"
};
var logconsole = new NLog.Targets.ConsoleTarget("logconsole"){
Layout = layout
};
nlogConf.AddRule(NLog.LogLevel.Trace, NLog.LogLevel.Fatal, logfile);
nlogConf.AddRule(NLog.LogLevel.Info, NLog.LogLevel.Fatal, logconsole);
NLog.LogManager.Configuration = nlogConf;
NLog.LogManager.ReconfigExistingLoggers();
}
}
}
这里最主要的是StartProcess()方法中的参数设置,有下面几种配置方式:
| CreateNoWindow | UseShellExecute | 结果 | |
|---|---|---|---|
| 1. | false | true | 新建一个窗口,然后在新建的窗口运行子进程。主进程正常退出,子进程继续运行。 |
| 2. | false | false | 在当前窗口运行子进程。关闭当前窗口后,主进程退出,开启的子进程也退出。 |
| 3. | true | false | 当前窗口主进程正常退出。子进程在后台执行,不开启新窗口,需要通过任务管理器关闭子进程。 |
| 4. | true | true | 和第1种情况相同 |
从上面可见,对于这里的需求而言,第3中情况比较符合。
因为使用了NLog以及Configuration,如果要运行程序,需要在项目文件中添加一下引用的程序集:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0"/>
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.2.0"/>
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0"/>
<PackageReference Include="NLog" Version="4.5.10"/>
</ItemGroup>
<ItemGroup>
<Content Include="*.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="*.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
最后,运行程序:
# dotnet AppStarter.dll 11:58:51|Info|控制台启动器运行, 环境:Development, 同步数据库:ShopUser, ShopLog, ShopCenter, CreateNoWindow:True, UseShellExecute:False 11:58:51|Info|开启应用: DataSync.dll ShopUser,进程id:7612 11:58:51|Info|开启应用: DataSync.dll ShopLog,进程id:7316 11:58:51|Info|开启应用: DataSync.dll ShopCenter,进程id:4868
可以看到类似下面的结果:

在上图中,我后台运行了6个控制台程序(注意代码中只有3个,这里有点图文不符)。
感谢阅读,希望这篇文章能给你带来帮助!