• .NET Core 代码安装服务启动


    最近做了一些.NET Core的程序,有在Windows下运行的 有在CentOS 下运行的,Windows下运行的还好,对Windows下还算比较熟悉了,但CentOS 下 每次都是找笔记支持命令

    于是今天晚上就干脆把以.NET Core程序已服务形式启动的代码封装了下,代码 主要是便于安装。

    我们写好一个程序后 然后要已服务启动 每次都是需要输入命令,对于我这种不记单词的人来说太痛苦了,

    当然windows环境下命令到是很简单。

    废话不说了 先给大家看下效果

    CentOS 下的运行效果是这样的

    dotnet ConsoleApp2.dll -i 

    进行服务的安装

    dotnet ConsoleApp2.dll -u

    进行服务的卸载

    安装完成后使用 ps x 进行进程查看 是否存在。

    Window 下运行效果

     安装完成,我们在到window 服务管理工具去看看

    发现已经多了一个服务,说明安装成功了

    然后再来分析代码

    首先CentOS 下让程序以服务形式启动 我们需要做以下工作

    1、写service文件

    2、systemctl 启动service

    service文件内容如下:

    [Unit]
    Description="服务说明"      
    [Service]
    Type=simple
    GuessMainPID=true
    WorkingDirectory=//项目路径
    StandardOutput=journal
    StandardError=journal
    ExecStart=/usr/bin/dotnet 项目文件dll  //启动指令
    Restart=always
    
    [Install]
    WantedBy=multi-user.target

    参考:http://www.jinbuguo.com/systemd/systemd.service.html

    在使用systemctl 命令使其生效

    systemctl enable *.service 使自启动生效
    systemctl start *.service 立即启动项目服务

    那么结合代码就好说了

     var servicepath = $"/etc/systemd/system/{serviceName}.service";// 创建服务文件
                        System.IO.File.WriteAllText(servicepath, $"[Unit]{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"Description={serviceDescription}{Environment.NewLine}");// 服务描述
                        System.IO.File.AppendAllText(servicepath, $"[Service]{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"Type=simple{Environment.NewLine}");//设置进程的启动类型, 必须设为 simple, forking, oneshot, dbus, notify, idle 之一。
                        System.IO.File.AppendAllText(servicepath, $"GuessMainPID=true{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"WorkingDirectory={workingDirectory}{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"StandardOutput=journal{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"StandardError=journal{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"ExecStart=/usr/bin/dotnet {System.IO.Path.GetFileName(filePath)}{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"Restart=always{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"[Install]{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"WantedBy=multi-user.target{Environment.NewLine}");
                        Console.WriteLine(StartProcess("/usr/bin/systemctl", $"enable {serviceName}.service"));
                        Console.WriteLine(StartProcess("/usr/bin/systemctl", $"start {serviceName}.service"));
                        Console.WriteLine($"Unix 下安装服务完成,如果失败请手动执行以下命令完成安装:");
                        Console.WriteLine($"systemctl enable {serviceName}.service  //使自启动生效");
                        Console.WriteLine($"systemctl start {serviceName}.service  //立即启动项目服务");
                        Console.WriteLine($"systemctl status {serviceName}.service -l //查看服务状态");
    View Code

    这样 我们就可以安装并启动服务了,

    那么windows下面 其实根据简单 ,就一个命令

    sc  create  服务名称 binpath=启动文件 就可以了

    当然 需要添加一个ServiceBase的子类,这里我搞了一个通用的

    代码如下

    /// <summary>
        /// Windows 服务
        /// </summary>
        class WinService : ServiceBase
        {
            private static string WinServiceName;
            private static Action<string[]> StartRun;
            public WinService()
            {
                ServiceName = WinServiceName;
            }
            /// <summary>
            /// 启动服务
            /// </summary>
            /// <param name="args"></param>
            protected override void OnStart(string[] args)
            {
                StartRun(args);
            }
            public static void Config(Action<string[]> startRun,string serviceName)
            {
                WinServiceName = serviceName;
                StartRun = startRun;
            }
        }
    View Code

    这里为什么是Action<string[]> StartRun; 了 就是因为让服务启动后 执行实际要执行的代码。

    比如demo

    class Program
        {
            [Description("这是一个测试服务")]
            [DisplayName("测试服务")]
            static void Main(string[] args)
            {
                DotNet.Install.ServiceInstall.Run("test", args, Run);
            }
            static void Run(string[] args)
            {
                HttpServer httpServer = new HttpServer();
                httpServer.Start();
            }
        }
    View Code

    服务启动后是执行的Run方法。

    WIndows下的安装代码那就是

    /// <summary>
            /// Windows 环境下运行
            /// </summary>
            /// <param name="filePath">启动文件</param>
            /// <param name="serviceName">服务名称</param>
            /// <param name="displayName">显示名称</param>
            /// <param name="serviceDescription">服务说明</param>
            /// <param name="args"></param>
            /// <param name="startRun">实际运行方法</param>
            /// <returns></returns>
            static bool RunWin(string filePath, string serviceName,string displayName, string serviceDescription, string[] args, Action<string[]> startRun)
            {
                if (args.Length == 1)
                {
                    var workingDirectory = System.IO.Path.GetDirectoryName(filePath);
                    if (System.IO.File.Exists(System.IO.Path.ChangeExtension(filePath, ".exe")))//判断是否存在exe ,如果存在则启动exe
                    {
                        filePath = System.IO.Path.ChangeExtension(filePath, ".exe");//修改后缀名为exe
                    }
                    else
                    {
                        var dotnetPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet", "dotnet.exe");//查找默认软件安装目录下的dotnet.exe
                        if (System.IO.File.Exists(dotnetPath))
                        {
                            filePath = $"\"{dotnetPath}\" \"{filePath}\"";
                        }
                        else
                        {
                            dotnetPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "dotnet", "dotnet.exe");//为了防止有的人只装了x86位的,所以查找x86下的软件安装目录下的dotnet.exe
                            if (System.IO.File.Exists(dotnetPath))
                            {
                                filePath = $"\"{dotnetPath}\" \"{filePath}\"";
                            }
                            else
                            {
                                Console.WriteLine($"系统无法定位DotNet Core的安装目录。");
                                return true;
                            }
                        }
                    }
                    if (args[0].Equals("-i", StringComparison.OrdinalIgnoreCase))
                    {
                        if (!AdminRestartApp(filePath, args))
                        {
                            return true;
                        }
                        Console.WriteLine(StartProcess("sc.exe", $"create {serviceName} binpath="{filePath}" start=auto DisplayName="{displayName}""));
                        Console.WriteLine($"Windows 下安装服务完成,如果失败请手动执行以下命令完成安装:");
                        Console.WriteLine($"sc create {serviceName} binpath="{filePath}" start=auto DisplayName="{displayName}" //安装服务");
                        using (var service = Registry.LocalMachine.OpenSubKey($@"SYSTEMCurrentControlSetServices{serviceName}", true))
                        {
                            service.SetValue("Description", serviceDescription);
                        }
                        return true;
                    }
                    else if (args[0].Equals("-u", StringComparison.OrdinalIgnoreCase))
                    {
                        if (!AdminRestartApp(filePath, args))
                        {
                            return true;
                        }
                        Console.WriteLine(StartProcess("sc.exe", $"delete {serviceName}"));
                        Console.WriteLine($"Windows 下卸载服务完成,如果失败请手动执行以下命令完成卸载:");
                        Console.WriteLine($"sc delete {serviceName}  //卸载服务");
                        return true;
                    }
                }
                WinService.Config(startRun, serviceName);
                using (var service = new WinService())
                {
                    System.ServiceProcess.ServiceBase.Run(service);
                }
                return false;
            }
    View Code

    这样我们就完美了

    在放上整个代码:

    /**************************************************************
    * Copyright (C) 2019 www.hnlyf.com 版权所有(盗版必究)
    *
    * 作者: 李益芬(QQ 12482335)
    * 创建时间: 2019/09/22 22:40:15
    * 文件名: 
    * 描述: 
    * 
    * 修改历史
    * 修改人:
    * 时间:
    * 修改说明:
    *
    **************************************************************/
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Text;
    using System.Linq;
    using System.ComponentModel;
    using Microsoft.Win32;
    
    namespace DotNet.Install
    {
        /// <summary>
        /// 服务安装
        /// </summary>
        public static class ServiceInstall
        {
            /// <summary>
            /// Windows 环境下运行
            /// </summary>
            /// <param name="filePath">启动文件</param>
            /// <param name="serviceName">服务名称</param>
            /// <param name="displayName">显示名称</param>
            /// <param name="serviceDescription">服务说明</param>
            /// <param name="args"></param>
            /// <param name="startRun">实际运行方法</param>
            /// <returns></returns>
            static bool RunWin(string filePath, string serviceName,string displayName, string serviceDescription, string[] args, Action<string[]> startRun)
            {
                if (args.Length == 1)
                {
                    var workingDirectory = System.IO.Path.GetDirectoryName(filePath);
                    if (System.IO.File.Exists(System.IO.Path.ChangeExtension(filePath, ".exe")))//判断是否存在exe ,如果存在则启动exe
                    {
                        filePath = System.IO.Path.ChangeExtension(filePath, ".exe");//修改后缀名为exe
                    }
                    else
                    {
                        var dotnetPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), "dotnet", "dotnet.exe");//查找默认软件安装目录下的dotnet.exe
                        if (System.IO.File.Exists(dotnetPath))
                        {
                            filePath = $"\"{dotnetPath}\" \"{filePath}\"";
                        }
                        else
                        {
                            dotnetPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), "dotnet", "dotnet.exe");//为了防止有的人只装了x86位的,所以查找x86下的软件安装目录下的dotnet.exe
                            if (System.IO.File.Exists(dotnetPath))
                            {
                                filePath = $"\"{dotnetPath}\" \"{filePath}\"";
                            }
                            else
                            {
                                Console.WriteLine($"系统无法定位DotNet Core的安装目录。");
                                return true;
                            }
                        }
                    }
                    if (args[0].Equals("-i", StringComparison.OrdinalIgnoreCase))
                    {
                        if (!AdminRestartApp(filePath, args))
                        {
                            return true;
                        }
                        Console.WriteLine(StartProcess("sc.exe", $"create {serviceName} binpath="{filePath}" start=auto DisplayName="{displayName}""));
                        Console.WriteLine($"Windows 下安装服务完成,如果失败请手动执行以下命令完成安装:");
                        Console.WriteLine($"sc create {serviceName} binpath="{filePath}" start=auto DisplayName="{displayName}" //安装服务");
                        using (var service = Registry.LocalMachine.OpenSubKey($@"SYSTEMCurrentControlSetServices{serviceName}", true))
                        {
                            service.SetValue("Description", serviceDescription);
                        }
                        return true;
                    }
                    else if (args[0].Equals("-u", StringComparison.OrdinalIgnoreCase))
                    {
                        if (!AdminRestartApp(filePath, args))
                        {
                            return true;
                        }
                        Console.WriteLine(StartProcess("sc.exe", $"delete {serviceName}"));
                        Console.WriteLine($"Windows 下卸载服务完成,如果失败请手动执行以下命令完成卸载:");
                        Console.WriteLine($"sc delete {serviceName}  //卸载服务");
                        return true;
                    }
                }
                WinService.Config(startRun, serviceName);
                using (var service = new WinService())
                {
                    System.ServiceProcess.ServiceBase.Run(service);
                }
                return false;
            }
            /// <summary>
            /// Unix环境下运行
            /// </summary>
            /// <param name="filePath"></param>
            /// <param name="serviceName"></param>
            /// <param name="serviceDescription"></param>
            /// <param name="args"></param>
            /// <param name="startRun"></param>
            /// <returns></returns>
            static bool RunUnix(string filePath, string serviceName, string serviceDescription, string[] args, Action<string[]> startRun)
            {
                var workingDirectory = System.IO.Path.GetDirectoryName(filePath);
                if (args.Length == 1)
                {
                    if (args[0].Equals("-i", StringComparison.OrdinalIgnoreCase))
                    {
                        var servicepath = $"/etc/systemd/system/{serviceName}.service";// 创建服务文件
                        System.IO.File.WriteAllText(servicepath, $"[Unit]{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"Description={serviceDescription}{Environment.NewLine}");// 服务描述
                        System.IO.File.AppendAllText(servicepath, $"[Service]{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"Type=simple{Environment.NewLine}");//设置进程的启动类型, 必须设为 simple, forking, oneshot, dbus, notify, idle 之一。
                        System.IO.File.AppendAllText(servicepath, $"GuessMainPID=true{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"WorkingDirectory={workingDirectory}{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"StandardOutput=journal{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"StandardError=journal{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"ExecStart=/usr/bin/dotnet {System.IO.Path.GetFileName(filePath)}{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"Restart=always{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"[Install]{Environment.NewLine}");
                        System.IO.File.AppendAllText(servicepath, $"WantedBy=multi-user.target{Environment.NewLine}");
                        Console.WriteLine(StartProcess("/usr/bin/systemctl", $"enable {serviceName}.service"));
                        Console.WriteLine(StartProcess("/usr/bin/systemctl", $"start {serviceName}.service"));
                        Console.WriteLine($"Unix 下安装服务完成,如果失败请手动执行以下命令完成安装:");
                        Console.WriteLine($"systemctl enable {serviceName}.service  //使自启动生效");
                        Console.WriteLine($"systemctl start {serviceName}.service  //立即启动项目服务");
                        Console.WriteLine($"systemctl status {serviceName}.service -l //查看服务状态");
                        return true;
                    }
                    else if (args[0].Equals("-u", StringComparison.OrdinalIgnoreCase))
                    {
                        var servicepath = $"/etc/systemd/system/{serviceName}.service";
                        Console.WriteLine(StartProcess("/usr/bin/systemctl", $"disable {serviceName}.service"));
                        if (System.IO.File.Exists(servicepath))
                        {
                            System.IO.File.Delete(servicepath);
                        }
                        Console.WriteLine($"Unix 下卸载服务完成,如果失败请手动执行以下命令完成卸载");
                        Console.WriteLine($"systemctl disable {serviceName}.service  //使自启动失效");
                        Console.WriteLine($"rm -y {servicepath}  //删除服务文件");
                        return true;
                    }
                }
                startRun(args);
                System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite);
                return false;
            }
            /// <summary>
            /// 运行程序,如果有命令-i或者-u则执行安装或卸载,否则执行<paramref name="startRun"/>
            /// <para>请在Main函数中调用,服务显示名称请在Main函数增加[DisplayName()]特性,服务说明[Description]特性。</para>
            /// <para>Windiows下需要依赖System.ServiceProcess.ServiceController</para>
            /// <para>-i 表示安装服务</para>
            /// <para>-u 表示卸载服务</para>
            /// </summary>
            /// <param name="serviceName">服务名称</param>
            /// <param name="args">启动程序的参数</param>
            /// <param name="startRun">实际程序运行的函数</param>
            public static void Run(string serviceName, string[] args, Action<string[]> startRun)
            {
                bool finish = false;
                string serviceDescription = serviceName;
                string displayName = serviceName;
                string filePath = string.Empty;
                if (args.Length == 1)
                {
                    StackFrame frame = new StackFrame(1);
                    if (string.IsNullOrWhiteSpace(serviceName))
                    {
                        serviceName=frame.GetMethod().DeclaringType.Assembly.GetName().Name;
                    }
                    var displayNames = frame.GetMethod().GetCustomAttributes(typeof(DisplayNameAttribute), true);
                    if (displayNames.Length > 0)
                    {
                        displayName = (displayNames[0] as DisplayNameAttribute).DisplayName;
                    }
                    var descriptions = frame.GetMethod().GetCustomAttributes(typeof(DescriptionAttribute), true);
                    if (descriptions.Length > 0)
                    {
                        serviceDescription = (descriptions[0] as DescriptionAttribute).Description;
                    }
                    filePath = frame.GetMethod().DeclaringType.Assembly.Location;
                }
                if (Environment.OSVersion.Platform == PlatformID.Win32NT)
                {
                    finish = RunWin(filePath, serviceName, displayName, serviceDescription, args, startRun);
                }
                else
                {
                    finish = RunUnix(filePath, serviceName, serviceDescription, args, startRun);
                }
            }
            static string StartProcess(string fileName, string arguments)
            {
                string output = string.Empty;
                using (System.Diagnostics.Process process = new System.Diagnostics.Process())
                {
                    process.StartInfo = new ProcessStartInfo
                    {
                        UseShellExecute = false,
                        Arguments = arguments,
                        RedirectStandardInput = true,
                        RedirectStandardOutput = true,
                        RedirectStandardError = true,
                        CreateNoWindow = true,
                        WorkingDirectory = Environment.CurrentDirectory,
                        FileName = fileName,
                    };
                    process.Start();//启动程序
                    process.WaitForExit();//等待程序执行完退出进程
                    output = process.StandardOutput.ReadToEnd();
                    process.Close();
                }
                return output;
            }
            static bool AdminRestartApp(string filePath,string[] args)
            {
                if (!IsAdmin())
                {
                    Console.WriteLine("重新已管理员启动" + filePath);
                    ProcessStartInfo startInfo = new ProcessStartInfo
                    {
                        UseShellExecute = true,
                        Arguments = string.Join(" ", args),
                        WorkingDirectory = Environment.CurrentDirectory,
                        FileName = filePath,
                        Verb = "runas"
                    };
                    try
                    {
                        Process.Start(startInfo);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"重新已管理员启动失败:{ex}");
                    }
                    return false;
                }
                return true;
            }
            /// <summary>
            /// 判断是否是处于Administrator下允许
            /// </summary>
            /// <returns></returns>
            static bool IsAdmin()
            {
                using (System.Security.Principal.WindowsIdentity wi = System.Security.Principal.WindowsIdentity.GetCurrent())
                {
                    System.Security.Principal.WindowsPrincipal wp = new System.Security.Principal.WindowsPrincipal(wi);
                    return wp.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator);
                }
            }
        }
    }
    View Code

    最后把dll 上转到nuget 上面 DotNetCN.Install

    源代码放到github上面:https://github.com/hnlyf/DotNet.Install

  • 相关阅读:
    五种排序算法
    call,apply,bind实现
    js面试题
    Date日期方法
    操作符
    var、let和const
    script标签属性
    HbuilderX如何让项目运行到微信开发者工具
    vite搭建vue3项目
    瀑布流实例及懒加载(echo.js)
  • 原文地址:https://www.cnblogs.com/dotnet-org-cn/p/11565702.html
Copyright © 2020-2023  润新知