欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

.NET Core 代码安装服务启动

程序员文章站 2022-04-14 22:58:55
最近做了一些.NET Core的程序,有在Windows下运行的 有在CentOS 下运行的,Windows下运行的还好,对Windows下还算比较熟悉了,但CentOS 下 每次都是找笔记支持命令 于是今天晚上就干脆把以.NET Core程序已服务形式启动的代码封装了下,代码 主要是便于安装。 我 ......

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

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

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

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

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

centos 下的运行效果是这样的

dotnet consoleapp2.dll -i 

 

进行服务的安装

dotnet consoleapp2.dll -u

进行服务的卸载

.NET Core 代码安装服务启动

 

.NET Core 代码安装服务启动

 

 

 

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

window 下运行效果

.NET Core 代码安装服务启动

 

 

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

.NET Core 代码安装服务启动

 

.NET Core 代码安装服务启动

 

 

 

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

 

然后再来分析代码

首先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

参考:

在使用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 //查看服务状态");

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

那么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;
        }
    }

这里为什么是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();
        }
    }

服务启动后是执行的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;
        }

这样我们就完美了

在放上整个代码:

/**************************************************************
* 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);
            }
        }
    }
}

最后把dll 上转到nuget 上面 dotnetcn.install

源代码放到github上面:https://github.com/hnlyf/dotnet.install