说明书大全网 - 海量说明书在线查询
手机版  |  网站地图

在. Net 5如何使用Windows服务(net5零基础入门教程)


.Net 5/6提供新的创建Windows服务

创建项目选择:

在. Net 5如何使用Windows服务(net5零基础入门教程)(1)

在.Net 5及之后的版本使用新的创建Windows服务

在创建项目后,在nuget安装: Microsoft.Extensions.Hosting.WindowsServices

在. Net 5如何使用Windows服务(net5零基础入门教程)(2)

在Nuget种安装Microsoft.Extensions.Hosting.WindowsServices

这里主要是通过服务定时修改cpu的型号信息,是因为我的台式机当时买的是es(当时主要图便宜),在任务管理器中cpu型号是0000的.对于有强迫症的,可以对es的cpu忽略了.

在. Net 5如何使用Windows服务(net5零基础入门教程)(3)

没修改cpu型号信息

服务启动代码:

using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Hosting;using IHost host = Host.CreateDefaultBuilder(args)    //使用在Microsoft.Extensions.Hosting.WindowsServices提供的中间件 UseWindowsService    .UseWindowsService(options =>      {        //指定服务名称        options.ServiceName = "UpdateCPUService";     })    .ConfigureServices(services =>    {        //将UpdateCPUService注入到容器中        services.AddHostedService<UpdateCPUService.UpdateCPUService>();     })    .Build();await host.RunAsync();

具体服务代码:

using System.Runtime.Versioning;using Microsoft.Extensions.Hosting;using Microsoft.Extensions.Logging;using Microsoft.Win32;namespace UpdateCPUService{    public class UpdateCPUService : BackgroundService    {        private readonly ILogger<UpdateCPUService> _logger;        public UpdateCPUService(ILogger<UpdateCPUService> logger)        {            this._logger = logger;        }        //只支持Windows        [SupportedOSPlatform("windows")]        protected override async Task ExecuteAsync(CancellationToken stoppingToken)        {            //while (!stoppingToken.IsCancellationRequested)            //{            //    _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);            //    await Task.Delay(1000, stoppingToken);            //}            while (!stoppingToken.IsCancellationRequested)            {                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);                try                {                    //注册表路径:计算机HKEY_LOCAL_MACHINEHARDWAREDESCRIPTIONSystemCentralProcessor                    RegistryKey root = Registry.LocalMachine;                    RegistryKey? hardware = root.OpenSubKey("HARDWARE", true);                    if (hardware != null)                    {                        var description = hardware!.OpenSubKey("DESCRIPTION", true);                        var system = description!.OpenSubKey("System", true);                        //获取CentralProcessor节点                        var centralProcessor = system!.OpenSubKey("CentralProcessor", true);                        //子节点对应cpu核心数(包含超线程)                        for (int i = 0; i < centralProcessor!.SubKeyCount; i++)                        {                            RegistryKey? cpuNode = centralProcessor.OpenSubKey($"{i}", true);                            //修改cpu型号信息,这里不考虑灵活性,写死,可以加配置文件                            cpuNode!.SetValue("ProcessorNameString", "Intel(R) Core(TM) i9-10900 CPU @ 2.50GHz");                        }                    }                }                catch (Exception ex)                {                    _logger.LogError("Worker running at: {message}", ex.Message);                }                //测试为1分钟,正式为30分钟                await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);            }        }    }}

创建Windows服务相关命令:

#通过sc create创建Windows服务sc.exe create "UpdateCPUService" binpath="D:/codes/csharp/UpdateCPUService/bin/Release/net6.0/UpdateCPUService.exe"#通过sc delete 删除Windows服务sc.ese delete "UpdateCPUService"#启动服务net start updatecpuservice#停止服务net stop updatecpuservice

服务运行后:

在. Net 5如何使用Windows服务(net5零基础入门教程)(4)

Windows服务修改cpu型号信息

注意: 因为操作注册表程序要有权限. .Net程序要提高权限的话,可以添加 应用程序清单文件(app.maniftest)

在. Net 5如何使用Windows服务(net5零基础入门教程)(5)

在.Net程序中添加应用程序清单文件,将程序提升权限为管理员权限

<!--使用管理员权限--><requestedExecutionLevel  level="requireAdministrator" uiAccess="false" />

新的实现方式是新瓶装老酒

先看看UseWindowsService源码:

public static IHostBuilder UseWindowsService(this IHostBuilder hostBuilder){    return UseWindowsService(hostBuilder, _ => { });}public static IHostBuilder UseWindowsService(this IHostBuilder hostBuilder, Action<WindowsServiceLifetimeOptions> configure){    if (WindowsServiceHelpers.IsWindowsService())    {        // Host.CreateDefaultBuilder uses CurrentDirectory for VS scenarios, but CurrentDirectory for services is c:WindowsSystem32.        hostBuilder.UseContentRoot(AppContext.BaseDirectory);        hostBuilder.ConfigureLogging((hostingContext, logging) =>        {            Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows));            logging.AddEventLog();        })        .ConfigureServices((hostContext, services) =>        {            Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows));            services.AddSingleton<IHostLifetime, WindowsServiceLifetime>();  //将WindowsServiceLifetime添加到容器中            services.Configure<EventLogSettings>(settings =>            {                Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows));                if (string.IsNullOrEmpty(settings.SourceName))                {                    settings.SourceName = hostContext.HostingEnvironment.ApplicationName;                }            });            services.Configure(configure);        });    }    return hostBuilder;}

中间件源码:

using System;using System.Runtime.Versioning;using System.ServiceProcess;using System.Threading;using System.Threading.Tasks;using Microsoft.Extensions.Logging;using Microsoft.Extensions.Options;namespace Microsoft.Extensions.Hosting.WindowsServices{    //WindowsServiceLifetime实现ServiceBase    //可以看到还是基于ServiceBase的封装    [SupportedOSPlatform("windows")]    public class WindowsServiceLifetime : ServiceBase, IHostLifetime    {        private readonly TaskCompletionSource<object> _delayStart = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously);        private readonly ManualResetEventSlim _delayStop = new ManualResetEventSlim();        private readonly HostOptions _hostOptions;        public WindowsServiceLifetime(IHostEnvironment environment, IHostApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory, IOptions<HostOptions> optionsAccessor)            : this(environment, applicationLifetime, loggerFactory, optionsAccessor, Options.Options.Create(new WindowsServiceLifetimeOptions()))        {        }        public WindowsServiceLifetime(IHostEnvironment environment, IHostApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory, IOptions<HostOptions> optionsAccessor, IOptions<WindowsServiceLifetimeOptions> windowsServiceOptionsAccessor)        {            Environment = environment ?? throw new ArgumentNullException(nameof(environment));            ApplicationLifetime = applicationLifetime ?? throw new ArgumentNullException(nameof(applicationLifetime));            Logger = loggerFactory.CreateLogger("Microsoft.Hosting.Lifetime");            if (optionsAccessor == null)            {                throw new ArgumentNullException(nameof(optionsAccessor));            }            if (windowsServiceOptionsAccessor == null)            {                throw new ArgumentNullException(nameof(windowsServiceOptionsAccessor));            }            _hostOptions = optionsAccessor.Value;            ServiceName = windowsServiceOptionsAccessor.Value.ServiceName;            CanShutdown = true;        }        private IHostApplicationLifetime ApplicationLifetime { get; }        private IHostEnvironment Environment { get; }        private ILogger Logger { get; }        public Task WaitForStartAsync(CancellationToken cancellationToken)        {            cancellationToken.Register(() => _delayStart.TrySetCanceled());            ApplicationLifetime.ApplicationStarted.Register(() =>            {                Logger.LogInformation("Application started. Hosting environment: {envName}; Content root path: {contentRoot}",                    Environment.EnvironmentName, Environment.ContentRootPath);            });            ApplicationLifetime.ApplicationStopping.Register(() =>            {                Logger.LogInformation("Application is shutting down...");            });            ApplicationLifetime.ApplicationStopped.Register(() =>            {                _delayStop.Set();            });            Thread thread = new Thread(Run);  //创建一个后台线程            thread.IsBackground = true;            thread.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)        {            // Avoid deadlock where host waits for StopAsync before firing ApplicationStopped,            // and Stop waits for ApplicationStopped.            Task.Run(Stop, CancellationToken.None);            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();            // Wait for the host to shutdown before marking service as stopped.            _delayStop.Wait(_hostOptions.ShutdownTimeout);            base.OnStop();        }        protected override void OnShutdown()        {            ApplicationLifetime.StopApplication();            // Wait for the host to shutdown before marking service as stopped.            _delayStop.Wait(_hostOptions.ShutdownTimeout);            base.OnShutdown();        }        protected override void Dispose(bool disposing)        {            if (disposing)            {                _delayStop.Set();            }            base.Dispose(disposing);        }    }}

总结

  1. 老的方式,单一继承ServiceBase,通用性特好,支持.Net Framework/.Net Core及.Net 5/6/7,简单的Windows服务直接使用.
  2. 新的中间件方式,只能在.Net 5及更高的版本使用,在复杂的Windows服务要好一些.可以使用容器,进行依赖注入