.NET Core 代碼安裝服務啓動

最近作了一些.NET Core的程序,有在Windows下運行的 有在CentOS 下運行的,Windows下運行的還好,對Windows下還算比較熟悉了,但CentOS 下 每次都是找筆記支持命令html

因而今天晚上就乾脆把以.NET Core程序已服務形式啓動的代碼封裝了下,代碼 主要是便於安裝。git

咱們寫好一個程序後 而後要已服務啓動 每次都是須要輸入命令,對於我這種不記單詞的人來講太痛苦了,github

固然windows環境下命令到是很簡單。windows

廢話不說了 先給你們看下效果ide

CentOS 下的運行效果是這樣的函數

dotnet ConsoleApp2.dll -i 

 

進行服務的安裝工具

dotnet ConsoleApp2.dll -u

進行服務的卸載測試

 

 

 

 

安裝完成後使用 ps x 進行進程查看 是否存在。ui

Window 下運行效果spa

 

 

 安裝完成,咱們在到window 服務管理工具去看看

 

 

 

 

發現已經多了一個服務,說明安裝成功了

 

而後再來分析代碼

首先CentOS 下讓程序以服務形式啓動 咱們須要作如下工做

一、寫service文件

二、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($@"SYSTEM\CurrentControlSet\Services\{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($@"SYSTEM\CurrentControlSet\Services\{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

相關文章
相關標籤/搜索