.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
参考:
在使用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
推荐阅读
-
linux下将编译安装的服务设置为开机启动
-
干货:.net core实现读取自定义配置文件,有源代码哦
-
asp.net core系列 74 Exceptionless服务端安装
-
asp.net下模态对话框关闭之后继续执行服务器端代码的问题
-
ASP.net中Core自定义View查找位置的实例代码
-
ASP.NET Core依赖注入系列教程之服务的注册与提供
-
在Asp.Net或.Net Core中配置使用MarkDown富文本编辑器有开源模板代码(代码是.net core3.0版本)
-
在Asp.Net Core中配置使用MarkDown富文本编辑器实现图片上传和截图上传(开源代码.net core3.0)
-
Linux服务器部署.Net Core笔记:四、安装Supervisor进程守护
-
.NET Core WebApi中如何实现多态数据绑定实例代码