C#编写windows服务
Posted 快乐在角落里
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#编写windows服务相关的知识,希望对你有一定的参考价值。
前言
1 目标方案
目标是不让小孩玩游戏,考虑了一些方案,都感觉不行,比如开机自动关机,禁用网卡,修改 host 文件,修改 iP 和 DNS。
开机自动关机这个是最简单的,只需要一个关机脚本就行了,再加入开机自启项就行了,但是这种方式就是电脑都不给用了,家长不会进PE的话很难修复,要用的时候就很着急,虽然对普通家长来说现在也没那么需要电脑。
剩下的都是一些关于网络控制的,主要是我不会写,特别是针对游戏服务器的地址,这些都不明确,我觉得写不了。还有就是完全不让上网的禁用网关,修改DNS等不太人性化,而且这种都比较好修复,不让上网还是太过分
2 确定方案
所以目标变成了小孩能上网,但是不能玩游戏,完全不能玩游戏其实也不太好办到,毕竟没指定是哪些游戏,市面上游戏那么多我根本控制不过来,扫描全盘游戏也要先知道这目录是游戏才行,感觉这个需求就不太好做了。
考虑到先后台循环杀掉一些游戏也可以,虽然没确定这是否有效,但是先做一部分出来也行
3 实现方案
搜了半天windows监控程序怎么写,Java总有控制台不太好,我要的是后台运行,然后就看了计划任务,但是这必须手动创建,用程序创建文档有点多,静不下心来看。
最后选择了windows服务,这种方式是最好的,后台程序开机自启很方便。
但是用什么实现也不好选择,一开始我是用Java写了一个 while 死循环每隔5秒钟去杀掉记事本程序,这只是测试。写完后感觉差不多了,然后百度 Java 程序怎么封装成 windows 服务,弄了好久都不行,使用的 java windows wrapper
来封装,但是我在虚拟机上测试的时候根本无法后台运行,运行一次就终止了,还有的就是一直安装不了服务,或者1067 进程意外终止,应该是 while 循环的问题,没搞定我就放弃了这种,总之就是心累,没想到连这么个简单的程序都写不好。
接着就用C++来写,写完编译完也是服务安装不上,还是和 Java 一样的问题,这些都没搞定我就放弃了。
然后就是百度搜到用C#写很容易,于是就试着写了一下,没想到可以了。
虽然 C# 的语法我不太知道,但是面向百度编程嘛,边写边测试就行了。
4 开发环境
Microsoft Visual Studio Community 2022 (64 位) - Current 版本 17.5.3
.Net Framework 4.7.2
5 测试环境
VMware 17 Pro 17.0.1 build-21139696
VMware Workstation 17 Pro - 辰羿\'S_Blog 可以从这位大佬的网站上下载,这个虚拟机安装windows的步骤网上就很多了,如果嫌麻烦的话也可以在本地机器上安装服务,主要是我之前测试自动关机的时候用windows比较方便,随时都可以改。
Windows 11 22H2
系统下载就是搜msdn下载就行了。需要注意的是我之前安装过 VMware 16,一直装不了windows 10最新版和windows11,搞了半天才知道是版本低了
实现步骤
1 项目初始化以及部分代码
基本上是跟着 基于C#实现Windows服务 来做的。创建项目的难点在于取名字,对于自己项目,取个名字我觉得太难了,跟着教程取 TestService 不太好,当然这些都能改,但是改的地方很多,改漏了也不好,想了一会,就叫 kspService (Kill Specify Process Service) 终止指定进程服务
跟着这些教程,也只是创建了windows服务,没有具体怎么实现功能的,只说了启动和停止会执行的函数,没说运行时间隔一段时间执行代码怎么做,这块百度了很久才知道怎么做。
1.1 创建新项目
1.2 配置新项目
1.3 目录结构
为了和项目名统一,将默认的 Service1 类修改成 KspService
1.4 添加安装程序
双击KspService.cs,在 KspService 设计界面右键,添加安装程序
添加完安装程序后,目录会多出一个 ProjectInstaller.cs 文件,还会出现这个文件的设计节目,界面上有两个组件,分别是 serviceProcessInstaller 和 serviceInstaller,通过右键组件然后点击属性来修改服务配置
1.5 修改 serviceProcessInstaller
可以通过操作服务运行于其中的进程的 Account 属性来设置安全性上下文。 此属性允许将服务设置为以下四种帐户类型之一:
User
,该帐户会导致系统在安装服务时提示输入有效的用户名和密码,并在网络上单个用户指定的帐户的上下文中运行;LocalService
,该帐户在用作本地计算机上的非特权用户的帐户的上下文中运行,并向任意远程服务器提供匿名凭据;LocalSystem
,该帐户在提供广泛本地权限的帐户的上下文中运行,并向任意远程服务器提供计算机凭据;NetworkService
,该帐户在用作本地计算机上的非特权用户的帐户的上下文中运行,并向任意远程服务器提供计算机凭据。
我选择修改为 LocalSystem
如何:为服务指定安全上下文 - .NET Framework | Microsoft Learn
1.6 修改 serviceInstaller
StartType | 说明 |
---|---|
Manual | 该服务必须在安装后手动启动 |
Automatic | 只要重启计算机,服务就将自行启动。 |
Disabled | 服务无法启动。 |
设置自动启动就行
填写描述信息和服务显示信息,DisplayName是从管理里面看服务的名称
其他更具体的说明可以参考微软文档
如何:将安装程序添加到服务应用程序 - .NET Framework | Microsoft Learn
1.7 查看默认服务功能
在 KspService.cs 设计界面右键选择查看代码,代码如下
很明显,这里面只有启动和停止的代码实现
2 编写主要功能
主要功能是每隔一段时间就杀掉游戏进程,这需要哪些函数呢
写入日志文件,简单起见不用什么框架,直接写入文件就行了
杀进程函数,使用百度搜到的杀进程函数
读取文件获取进程名称
暂停,继续功能,默认的服务有启动停止,增加一下暂停和继续来重新读取文件获取进程名称
循环任务,使用 System.Timers.Timer
来每隔一段时间执行一次杀进程函数
2.1 定义用到的参数
/* 使用了using System.Timers; */
private static Timer timer = new Timer(300000);
/* 日志目录,当前目录下的log目录 */
private static readonly string logPath = AppDomain.CurrentDomain.BaseDirectory + "//log";
/* 需要终止进程的进程字文件 */
private static readonly string blackListFile = AppDomain.CurrentDomain.BaseDirectory + "blacklist.txt";
/* 文件里的进程名集合 */
private static HashSet<string> blackListFileSet = new HashSet<string>();
/* 默认提供的进程名集合 */
private static HashSet<string> blackListInnerSet = new HashSet<string>
"steam",
"Steam",
"WeGame",
"GTA5",
"Warframe.x64",
"League of Legends",
"YuanShen",
"r5Apex",
"r5apex",
"dota",
"dota2",
"csgo"
;
2.2 写入日志
private void WriteLog(string logContent)
if (!Directory.Exists(logPath))
Directory.CreateDirectory(logPath);
using (FileStream stream = new FileStream(logPath + $"\\\\log_DateTime.Now.ToString("yyyyMMdd").log", FileMode.Append))
using (StreamWriter writer = new StreamWriter(stream))
writer.WriteLine($"DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") logContent");
2.3 杀进程
p.ProcessName.Equals(processName)
采用Equals是为了准确,使用Contains的话,关闭主程序后,当前所有进程快照中还存在包含关键字的进程,但是因为关闭关键字主程序后那些包含关键字的进程已经终止掉了,再终止就会抛异常。
private void KillProcess(string processName)
foreach (Process p in Process.GetProcesses())
//采用Equals是为了准确, 使用Contains的话有些进程关闭会附带关闭其他带关键字的程序
if (p.ProcessName.Equals(processName))
try
p.Kill();
p.WaitForExit();
WriteLog($"[p.ProcessName] process killed");
catch (Win32Exception e)
WriteLog(e.Message.ToString());
catch (InvalidOperationException e)
WriteLog(e.Message.ToString());
2.4 读取文件获取进程名称
private void ReadBlackListFile()
if (File.Exists(blackListFile))
WriteLog($"read blackListFile");
using (StreamReader reader = new StreamReader(blackListFile))
while (reader.Peek() != -1)
string prcessName = reader.ReadLine();
blackListFileSet.Add(prcessName);
WriteLog($"add [prcessName] to blacklist");
else
WriteLog($"[blackListFile] not found");
2.5 重写暂停继续
构造函数中设置属性 CanPauseAndContinue
为 true
public KspService()
InitializeComponent();
this.CanPauseAndContinue = true;
然后重写 OnContinue,OnPause,OnShutdown 函数
2.6 循环执行任务
先写一个支持 Timer 执行的函数,主要功能是循环两个集合,终止里面的关键字程序
不写成一个集合是因为不希望完全由文件配置来管理,而且暂停继续需要重写读取文件的内容,到时会清空集合,但是默认的不能清空,因此分开使用两个集合
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
foreach (string processName in blackListInnerSet)
KillProcess(processName);
foreach (string processName in blackListFileSet)
KillProcess(processName);
2.7 KspService.cs 全量代码
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
namespace kspService
public partial class KspService : ServiceBase
/* 使用了using System.Timers; 还可以将间隔设置为5000(5秒)在测试环境测试 */
private static Timer timer = new Timer(5000);
/* 日志目录,当前目录下的log目录 */
private static readonly string logPath = AppDomain.CurrentDomain.BaseDirectory + "//log";
/* 需要终止进程的进程字文件 */
private static readonly string blackListFile = AppDomain.CurrentDomain.BaseDirectory + "blacklist.txt";
/* 文件里的进程名集合 */
private static HashSet<string> blackListFileSet = new HashSet<string>();
/* 默认提供的进程名集合 */
private static HashSet<string> blackListInnerSet = new HashSet<string>
"steam",
"Steam",
"WeGame",
"GTA5",
"Warframe.x64",
"League of Legends",
"YuanShen",
"r5Apex",
"r5apex",
"dota",
"dota2",
"csgo"
;
public KspService()
InitializeComponent();
this.CanPauseAndContinue = true;
protected override void OnStart(string[] args)
WriteLog("KillSpecifyProcessService OnStart! ");
ReadBlackListFile();
timer.Elapsed += Timer_Elapsed;
timer.Start();
protected override void OnStop()
timer.Stop();
WriteLog("KillSpecifyProcessService OnStop! ");
protected override void OnContinue()
ReadBlackListFile();
timer.Start();
WriteLog("KillSpecifyProcessService OnContinue! ");
protected override void OnPause()
blackListFileSet.Clear();
timer.Stop();
WriteLog("KillSpecifyProcessService OnPause !");
protected override void OnShutdown()
timer.Stop();
WriteLog("KillSpecifyProcessService OnShutdown !");
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
foreach (string processName in blackListInnerSet)
KillProcess(processName);
foreach (string processName in blackListFileSet)
KillProcess(processName);
private void ReadBlackListFile()
if (File.Exists(blackListFile))
WriteLog($"Read blackListFile");
using (StreamReader reader = new StreamReader(blackListFile))
while (reader.Peek() != -1)
string prcessName = reader.ReadLine();
blackListFileSet.Add(prcessName);
WriteLog($"Add [prcessName] to the blackListFileSet");
else
WriteLog($"[blackListFile] Not Found! ");
private void WriteLog(string logContent)
if (!Directory.Exists(logPath))
Directory.CreateDirectory(logPath);
using (FileStream stream = new FileStream(logPath + $"\\\\log_DateTime.Now.ToString("yyyyMMdd").log", FileMode.Append))
using (StreamWriter writer = new StreamWriter(stream))
writer.WriteLine($"DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") logContent");
private void KillProcess(string processName)
foreach (Process p in Process.GetProcesses())
/* 采用Equals是为了准确 */
/* 使用Contains的话,关闭主程序后,当前所有进程快照中还存在包含关键字的进程 */
/* 但是因为关闭关键字主程序后那些包含关键字的进程已经终止掉了,再终止就会抛异常 */
if (p.ProcessName.Equals(processName))
try
p.Kill();
p.WaitForExit();
WriteLog($"[p.ProcessName] Process Killed");
catch (Win32Exception e)
WriteLog(e.Message.ToString());
catch (InvalidOperationException e)
WriteLog(e.Message.ToString());
执行脚本
1 创建并启动服务
主要是将程序复制到公共目录,然后通过 InstallUtil.exe 创建服务
@echo off
chcp 65001
if not exist "C:\\Users\\Public\\Documents\\StudyService\\backup" (
md C:\\Users\\Public\\Documents\\StudyService\\backup
)
if exist "C:\\Users\\Public\\Documents\\StudyService\\kspService.exe" (
copy /y C:\\Users\\Public\\Documents\\StudyService\\kspService.exe C:\\Users\\Public\\Documents\\StudyService\\backup
del C:\\Users\\Public\\Documents\\StudyService\\kspService.exe
copy %~dp0\\kspService.exe C:\\Users\\Public\\Documents\\StudyService
)else (
copy %~dp0\\kspService.exe C:\\Users\\Public\\Documents\\StudyService
)
if not exist "C:\\Users\\Public\\Documents\\StudyService\\blacklist.txt" (
copy %~dp0\\blacklist.txt C:\\Users\\Public\\Documents\\StudyService\\blacklist.txt
)
C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\InstallUtil.exe -i C:\\Users\\Public\\Documents\\StudyService\\kspService.exe
echo 创建服务成功!
sc start kspService
echo 启动服务成功!
pause
2 更新服务 暂停与继续
@echo off
sc pause kspService
sc continue kspService
3 启动服务
@echo off
sc start kspService
4 删除服务
@echo off
sc stop kspService
C:\\Windows\\Microsoft.NET\\Framework\\v4.0.30319\\InstallUtil.exe -u C:\\Users\\Public\\Documents\\StudyService\\kspService.exe
if exist "C:\\Users\\Public\\Documents\\StudyService\\kspService.exe" (
copy /y C:\\Users\\Public\\Documents\\StudyService\\kspService.exe C:\\Users\\Public\\Documents\\StudyService\\backup
del C:\\Users\\Public\\Documents\\StudyService\\kspService.exe
)
5 停止服务
@echo off
sc stop kspService
6 重启服务
@echo off
sc stop kspService
sc start kspService
测试
1 Visual Studio 右键项目生成 .exe 文件
2 我的exe文件在在该路径下
D:\\admin\\dev\\source\\repos\\kspService\\kspService\\bin\\Debug
3 将生成的程序和脚本复制到虚拟机任意目录下
复制到虚拟机需要先安装 VMware Tools 并重启windows虚拟机系统
复制到任意目录
4 右键 创建服务.bat 使用管理员身份运行
如图表示创建成功
5 检查服务是否运行
打开任务管理器选择详细信息发现服务已经运行了,还可以查看计算机管理服务选项,查看服务是否启动
管理里面查看服务名显示
6 在安装目录下修改 blacklist.txt
修改blacklist.txt,是为了测试暂停继续是否有效,而且也是为了以后增加需要关闭的进程不用重新生成程序
打开我指定的安装目录
C:\\Users\\Public\\Documents\\StudyService
编辑 blacklist.txt,增加记事本程序名称,在windows10下是 notepad
在windows11下是 Notepad
,区别就是win11的记事本程序N是大写的
7 操作演示
c#开发和学习(c#编写windows服务)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
大家有没有想过一些程序,属于那种开机即启动的。比如说web服务器程序,mysql程序等等。但是呢,这些程序本身又没有任何的console对话框,所以这个时候就要把他们编写成windows服务程序。window服务本身也是一个exe文件,中间的一部分函数功能需要做override处理,正是这些override的函数保证了这个service可以接受外界command的输入。
那么就说c#如何编写服务程序。事实上,不仅仅是c#,vb、f#、c、c++都可以编写服务程序。只是c#编写起来比较简单一点。本次实验的环境是win11+vs2017。
1、创建windows服务工程,
首先,在创建工程的时候选择windows 服务即可。因为我们选择的是基础的.net framework,所以这里也做了相应的Windows Service选项。
2、确认Program.cs文件
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
namespace WindowsService1
static class Program
/// <summary>
/// 应用程序的主入口点。
/// </summary>
static void Main()
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
new Service1()
;
ServiceBase.Run(ServicesToRun);
这部分是服务的入口点,相当于exe的主函数main。其主要作用是把Service1加入到整个ServicesToRun中。内容比较简单。这部分代码不需要做任何修改。
3、第一个服务Service1.cs文件
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
namespace WindowsService1
public partial class Service1 : ServiceBase
public Service1()
InitializeComponent();
protected override void OnStart(string[] args)
using (System.IO.StreamWriter sw = new System.IO.StreamWriter("D:\\\\info.txt", true))
sw.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "Start.");
protected override void OnStop()
using (System.IO.StreamWriter sw = new System.IO.StreamWriter("D:\\\\info.txt", true))
sw.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + "Stop.");
整个类继承自ServiceBase。结构上就是一个八股文,除了在构造函数中千篇一律调用InitializeComponent之外,剩下来的OnStart和OnStop就是用户自己需要添加内容的地方。OnStart对应net start命令,OnStop对应net stop命令。
这里为了功能说明,只是在OnStart和OnStop做了一些字符保存的动作。不出意外,在Service启动和推出的时候,会在D:多一个info.txt文件出来。
4、编译
和正常的项目编译一样,最终会生成一个可执行文件,即WindowsService1.exe。
5、注册服务
sc create WindowsService1 binpath="C:/Users/feixiaoxing/Desktop/WindowsService1/WindowsService1/bin/Debug/WindowsService1.exe" type=own start=auto displayname=WindowsService1
6、启动服务
net start WindowsService1
7、退出服务
net stop WindowsService1
8、确认d盘是否有文件生成
不出意外的话,应该在d盘有info.txt生成,文件内容如下所示,
2022-09-25 16:20:55 Start.
2022-09-25 16:21:10 Stop.
9、解除服务注册
sc delete “WindowsService1”
以上是关于C#编写windows服务的主要内容,如果未能解决你的问题,请参考以下文章