.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 //查看服务状态");
这样 我们就可以安装并启动服务了,
那么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 == )
{
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[].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[].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 == )
{
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[].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[].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 == )
{
if (args[].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[].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 == )
{
StackFrame frame = new StackFrame();
if (string.IsNullOrWhiteSpace(serviceName))
{
serviceName=frame.GetMethod().DeclaringType.Assembly.GetName().Name;
}
var displayNames = frame.GetMethod().GetCustomAttributes(typeof(DisplayNameAttribute), true);
if (displayNames.Length > )
{
displayName = (displayNames[] as DisplayNameAttribute).DisplayName;
}
var descriptions = frame.GetMethod().GetCustomAttributes(typeof(DescriptionAttribute), true);
if (descriptions.Length > )
{
serviceDescription = (descriptions[] 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
.NET Core 代码安装服务启动的更多相关文章
- win7 64位 mongodb2.6.0 安装服务启动
Workaround to install as a service You can manually install 2.6.0 as a service on Windows from an Ad ...
- c#创建windows服务(代码方式安装、启动、停止、卸载服务)
转载于:https://www.cnblogs.com/mq0036/p/7875864.html 一.开发环境 操作系统:Windows 10 X64 开发环境:VS2015 编程语言:C# .NE ...
- C#代码安装Windows服务(控制台应用集成Windows服务)
最近在为一款C/S架构的科研软件开发云计算版,需要用到WCF,考虑到不需要什么界面以及稳定性,无人值守性,准备用Windows Service作为宿主,无奈Windows Service的安装太为繁复 ...
- C# 编写windows服务及服务的安装、启动、删除、定时执行任务
一.编写windows服务 1.VS2017 - 创建服务Myservice 2.创建好项目之后 --- >> 双击 Service1.cs ---- >> 出现一个设计 ...
- Mysql 安装服务无法启动解决方案与使用的一般使用指令
问题描述: 在安装mysql 时,如果第一次没安装成功,第二次重装可能出现莫名奇妙的情况.通过一番尝试,发现,安装往上的教程还是无法成功安装的主要原因是第一次安装虽然未成功,但是mysql 默认的33 ...
- 手把手教用C#编写Windows服务 并控制服务 安装、启动、停止、卸载
Windows服务 Microsoft Windows 服务(即,以前的 NT 服务)使您能够创建在它们自己的 Windows 会话中可长时间运行的可执行应用程序.这些服务可以在计算机启动时自动启动, ...
- windows 服务的安装、启动、状态查询、停止操作c++实现
具体的自己看看代码 粘贴复制即可使用 卸载也很简单自己查看MSDN 加上就是 #ifndef __SERVICEMANAGE_H__ #define __SERVICEMANAGE_H__ #incl ...
- RedHat 7.0 下 FTP 服务的安装,启动,配置,以及虚拟用户的建立
(注意! 区分shell命令和往配置文件里加的代码不同) 一:ftp服务的安装,启动和启用. 1:vim /etc/sysconfig/selinux 改为disabled后重启 ...
- MySQL 安装和启动服务,“本地计算机 上的 MySQL 服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止。”
MySQL 安装和启动服务,以及遇到的问题 MySQL版本: mysql-5.7.13-winx64.zip (免安装,解压放到程序文件夹即可,比如 C:\Program Files\mysql-5. ...
随机推荐
- drf序列化与反序列化
序列化器-Serializer 定义序列化器 Django REST framework中的Serializer使用类来定义,须继承自rest_framework.serializers.Serial ...
- Java之路---Day18(List集合)
2019-11-05-23:03:28 List集合: java.util.List 接口继承自 Collection 接口,是单列集合的一个重要分支,习惯性地会将实现了List 接口的对象称为Lis ...
- Tomcat配置https加密连接
配置https安全连接(ssl加密连接) https连接需要用到数字证书与数字签名(MD5算法),网站https连接首先需要申请数字证书,配置加密连接器,浏览器安装证书. 证书运用到RSA技术,RSA ...
- Vue -- 项目报错整理(2):IE报错 - ‘SyntaxError:strict 模式下不允许一个属性有多个定义‘ ,基于vue element-ui页面跳转坑的解决
- 链接标签(a 标签)
一.链接标签 单词缩写: anchor 的缩写. 在HTML中创建超链接非常简单,只需用标签环绕需要被链接的对象即可. 语法格式: <a href="跳转目标" target ...
- MySQL--performance schema学习
启用performance schema 在MySQL 5.6.6版本后,performance schema被默认打开 通常MySQL的二进制版本都默认支持PS, 如果使用编译源码安装,在cmake ...
- jQuery循环之each()
/** *定义和用法:$(selector).each(function(index,element)) *each()函数会对每个匹配到的元素运行函数(返回false可终止循环). *each()函 ...
- 大数据:Hadoop(HDFS 读写数据流程及优缺点)
一.HDFS 写数据流程 写的过程: CLIENT(客户端):用来发起读写请求,并拆分文件成多个 Block: NAMENODE:全局的协调和把控所有的请求,提供 Block 存放在 DataNode ...
- 数据结构中的堆(Heap)
堆排序总结 这是排序,不是查找!!!查找去找二叉排序树等. 满二叉树一定是完全二叉树,但完全二叉树不一定是满二叉树. 构建顶堆: a.构造初始堆 b.从最后一层非叶节点开始调整,一直到根节点 c.如果 ...
- 前端模板引擎artTemplate.js
. 关于artTemplate模板引擎的详细原理请移步高性能JavaScript模板引擎原理解析,本文只探讨如何使用.初学前端的人一般对于绑定数据都是使用原生js或者jquery来拼接字符串,此为ha ...