如何开发并一键发布WindowsService项目(netcore普通项目)
netcore下开发windows服务如果是web项目的话,由于aspnetcore本身是支持的,把默认的host.Run改为host.RunAsService就可以了。
但是普通的netcore的控制台项目我终于找到了如下方式来实现:
1. 打开vs 选择创建一个新的netcore 控制台项目
Nuget添加如下引用
* Microsoft.Extensions.Hosting
* System.ServiceProcess.ServiceController
新建一个ServiceBaseLifetime.cs 并将下面的内容复制进去
public class ServiceBaseLifetime : ServiceBase, IHostLifetime { private
readonly TaskCompletionSource _delayStart = new TaskCompletionSource(); public
ServiceBaseLifetime(IApplicationLifetime applicationLifetime) {
ApplicationLifetime= applicationLifetime ?? throw new
ArgumentNullException(nameof(applicationLifetime)); }private
IApplicationLifetime ApplicationLifetime {get; } public Task
WaitForStartAsync(CancellationToken cancellationToken) {
cancellationToken.Register(()=> _delayStart.TrySetCanceled());
ApplicationLifetime.ApplicationStopping.Register(Stop);new Thread(Run).Start();
// Otherwise this would block and prevent IHost.StartAsync from finishing.
return _delayStart.Task; } private void Run() { try { Run(this); // This blocks
until the service is stopped. _delayStart.TrySetException(new
InvalidOperationException("Stopped without starting")); } catch (Exception ex)
{ _delayStart.TrySetException(ex); } }public Task StopAsync(CancellationToken
cancellationToken) { Stop();return Task.CompletedTask; } // Called by base.Run
when the service is ready to start. protected override void OnStart(string[]
args) { _delayStart.TrySetResult(null); base.OnStart(args); } // Called by
base.Stop. This may be called multiple times by service Stop,
ApplicationStopping, and StopAsync.// That's OK because StopApplication uses a
CancellationTokenSource and prevents any recursion. protected override void
OnStop() { ApplicationLifetime.StopApplication();base.OnStop(); } }
新建一个ServiceBaseLifetimeHostExtensions.cs 并将下面的内容复制进去
public static class ServiceBaseLifetimeHostExtensions { public static
IHostBuilder UseServiceBaseLifetime(this IHostBuilder hostBuilder) { return
hostBuilder.ConfigureServices((hostContext, services) =>
services.AddSingleton<IHostLifetime, ServiceBaseLifetime>()); } public static
void RunAsService(this IHostBuilder hostBuilder) {
hostBuilder.UseServiceBaseLifetime().Build().Run(); }public static Task
RunAsServiceAsync(this IHostBuilder hostBuilder) { return
hostBuilder.UseServiceBaseLifetime().Build().RunAsync(CancellationToken.None);
} }
新建一个服务类 TestService.cs 并写入以下内容(该服务就是每1秒往d:\log.txt写入当前时间)
public class TestService: IHostedService,IDisposable { readonly
System.Timers.Timer tmBak =new System.Timers.Timer(); public TestService() {
tmBak.Interval= 1000;//1秒执行1次 tmBak.AutoReset = true;//执行1次false,一直执行true
tmBak.Enabled =true; tmBak.Elapsed += (sender, eventArgs) => { using
(StreamWriter sw =new StreamWriter("D:\\log.txt",true)) { sw.WriteLine($"
AntDeploy Windows服务:{DateTime.Now:yyyy-MM-dd HH:mm:ss}"); } }; } public Task
StartAsync(CancellationToken cancellationToken) { tmBak.Start();return
Task.CompletedTask; }public Task StopAsync(CancellationToken cancellationToken)
{ tmBak.Stop();return Task.CompletedTask; } public void Dispose() { this
.tmBak.Dispose(); } }
编辑 Program.cs 写入如下内容,保证既可以作为控制台打开又可以作为windows服务运行:
class Program { // P/Invoke declarations for Windows. [DllImport("kernel32.dll"
)]static extern IntPtr GetConsoleWindow(); [DllImport("user32.dll")] static
extern bool IsWindowVisible(IntPtr hWnd); public static bool
HaveVisibleConsole() {return
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
IsWindowVisible(GetConsoleWindow()) : Console.WindowHeight> 0; } private static
async Task Main(string[] args) { var pathToExe =
Process.GetCurrentProcess().MainModule.FileName;var pathToContentRoot =
Path.GetDirectoryName(pathToExe);
Directory.SetCurrentDirectory(pathToContentRoot);var isService =
!(Debugger.IsAttached || args.Contains("--console")); if (HaveVisibleConsole())
isService =false; var builder = new HostBuilder()
.ConfigureServices((hostContext, services)=> { services.AddHostedService
<TestService>(); }); if (isService) { await builder.RunAsServiceAsync(); } else
{await builder.RunConsoleAsync(); } } }
一键发布到服务器,在工程上点击右键 然后选择 AntDeploy
AntDeploy是我开发的一款开源一键部署vs插件(也是支持脱离vs单独使用的一个开源工具)
开源地址:https://github.com/yuzd/AntDeployAgent
<https://github.com/yuzd/AntDeployAgent>
配置AntDeploy
添加一个环境 名字叫 测试 然后 在 测试环境里面添加 windows服务器 这里我做测试就添加就是我本机,注意Host里面是填写格式为:ip:端口号
注意:Token不是windows服务器的密码!!!是安装agent后,agent的配置文件里面配置的Token(你自己自定义配置的)
注意:Port不是你要发布的项目的端口号!!!是安装agent后,agent的配置文件里面配置的端口号(你自己自定义配置的)
点击【Connect Test】按钮进行确认agent可以成功链接,否则会发布失败
如果【Connect Fail】失败 请查看 #10 <https://github.com/yuzd/AntDeployAgent/issues/10>
进入 WindowsService Tab界面
Sdk类型选择 netcore
ServiceName 填写上面我们设置的名称:[TestService]
点击 【Deploy】按钮进行发布
确认服务器无误 点击 【是】开始执行一键部署
如果发布出现错误会出现下图所示:
可以在Log里面查看失败原因是因为我部署agent没有用管路员权限 报权限不足失败 需要用管理员权限运行agent才行
部署成功 如下图:
查看D盘下是否log.txt是否正常每隔1秒写入了当前时间
这里演示的是windows服务上没有这个service
所以自动创建了。
如果service已存在的情况 Deploy 就会全量覆盖 不会重新创建site的。
如果想要覆盖时排除指定文件 可以在 Setting Tab界面的IgnoreList里面增加(支持正则)
热门工具 换一换