目录
- [一. 需求](#一. 需求)
- [二. 初始化项目](#二. 初始化项目)
- [三. 程序代码](#三. 程序代码)
-
- [3.1 `appsettings.json` 配置文件](#3.1
appsettings.json配置文件) - [3.2 `ProcessMonitorOptions.cs` 配置类](#3.2
ProcessMonitorOptions.cs配置类) - [3.3 `Program.cs` 主程序](#3.3
Program.cs主程序) - [3.4 `Worker.cs` 工作类](#3.4
Worker.cs工作类) - [3.5 说明](#3.5 说明)
- [3.1 `appsettings.json` 配置文件](#3.1
- [四. 发布](#四. 发布)
-
- [4.1 修改`.csproj`配置文件](#4.1 修改
.csproj配置文件) - [4.2 发布](#4.2 发布)
- [4.1 修改`.csproj`配置文件](#4.1 修改
一. 需求
- 检测当前用户运行的线程,一旦检测到指定的程序,就强行杀死程序对应的线程。
- 该程序需要一直运行,不使用脚本的方式,需要创建一个.exe文件运行在后台。
- 该程序不需要UI界面
- 创建
console项目的话,会有控制台窗口显示 - 创建
worker项目的话,没有控制台窗口
- 创建
二. 初始化项目
worker 模板创建的是一个长期运行的后台服务程序(类似 Windows 服务 / Linux daemon)
🔷核心特点:
- 基于
.NET Generic Host - 默认支持:
- 日志(Logging)
- 依赖注入(DI)
- 配置(appsettings.json)
🔷适合:
- 后台监控
- 定时任务
- 消息队列消费者
- 系统服务
csharp
// 创建项目后, 移动到项目中
dotnet new worker -n MyMonitorService
cd MyMonitorService
// 安装依赖
dotnet add package Microsoft.Extensions.Hosting
🔷项目创建完之后,修改MyMonitorService.csproj文件
- 默认情况下自动开启自动
Using,先将其关闭,手动Using - 将
<ImplicitUsings>enable</ImplicitUsings>改为<ImplicitUsings>disable</ImplicitUsings>
三. 程序代码
3.1 appsettings.json 配置文件
- 配置要监测的程序的线程名字
- 最长存活时间
- 每次监测的间隔
json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"ProcessMonitor": {
"ProcessNames": [ "Weixin", "Hidemaru" ],
"MaxMinutes": 1,
"CheckIntervalSeconds": 10
}
}
3.2 ProcessMonitorOptions.cs 配置类
🔷用于存放从配置文读取到的信息的配置类
csharp
using System.Diagnostics.CodeAnalysis;
namespace MyMonitorService;
// 为了防止Trim发布时的误删
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
public class ProcessMonitorOptions
{
public string[] ProcessNames { get; set; } = [];
public int MaxMinutes { get; set; } = 1;
public int CheckIntervalSeconds { get; set; } = 10;
}
3.3 Program.cs 主程序
csharp
using System.Threading;
using MyMonitorService;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection;
Mutex? mutex = null;
try
{
mutex = new Mutex(true, "MyMonitorService", out bool createdNew);
if (!createdNew)
{
return;
}
// 获取当前Host的Builder对象
IHostBuilder builder = Host.CreateDefaultBuilder(args);
// 为Builder对象添加服务
builder.ConfigureServices((context, services) =>
{
// 添加服务所需的配置文件类
services.Configure<ProcessMonitorOptions>(
context.Configuration.GetSection("ProcessMonitor")
);
// 添加服务所需的Worker
services.AddHostedService<Worker>();
});
IHost host = builder.Build();
host.Run();
}
finally
{
mutex?.ReleaseMutex();
mutex?.Dispose();
}
3.4 Worker.cs 工作类
csharp
using System;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
// 更加简洁的定义namespace的方法
namespace MyMonitorService;
public class Worker(ILogger<Worker> logger, IOptionsMonitor<ProcessMonitorOptions> optionsMonitor) : BackgroundService
{
// 日志对象
private readonly ILogger<Worker> _logger = logger;
// 配置文件对象
private readonly IOptionsMonitor<ProcessMonitorOptions> _optionsMonitor = optionsMonitor;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
// 每次循环都获取最新的配置
var options = _optionsMonitor.CurrentValue;
// 读取配置文件中的线程名
foreach (var processName in options.ProcessNames)
{
// 获取线程名对象
var processes = Process.GetProcessesByName(processName);
foreach (var proc in processes)
{
// 如果当前线程的运行时间 < 指定的最大时间的话
var runTime = DateTime.Now - proc.StartTime;
if (runTime.TotalMinutes < options.MaxMinutes)
{
// 跳过, 不杀死线程
continue;
}
// 杀死指定的线程
try
{
_logger.LogWarning("Killing {Process} PID={Pid}", processName, proc.Id);
proc.Kill();
}
catch (Exception ex)
{
_logger.LogError(ex, "Kill error: {Process}", processName);
}
}
}
// 延时
await Task.Delay(options.CheckIntervalSeconds * 1000, stoppingToken);
}
catch (TaskCanceledException)
{
return;
}
catch (Exception ex)
{
_logger.LogError(ex, "Main loop error");
}
}
}
}
3.5 说明
🔷传统写法
csharp
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly IOptionsMonitor<ProcessMonitorOptions> _options;
public Worker(
ILogger<Worker> logger,
IOptionsMonitor<ProcessMonitorOptions> options)
{
_logger = logger;
_options = options.CurrentValue;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
。。。省略。。。
}
}
🔷C#12的 主构造函数(Primary Constructor),类定义时直接声明构造函数参数。
-
services.AddHostedService()
→ 把 Worker 注册为 IHostedService
-
.NET在启动 Host 时:→ 从 DI 容器解析 IHostedService
-
DI容器创建 Worker 实例时:→ 分析构造函数参数(构造函数注入)
-
发现需要:
ILogger<Worker>IOptionsMonitor<ProcessMonitorOptions>
-
容器去查:
ILogger<Worker>→ 已由CreateDefaultBuilder注册IOptionsMonitor<T>→ 由Configure<T>()注册
-
然后实例化:
new Worker(logger, optionsMonitor)
csharp
public class Worker(ILogger<Worker> logger, IOptionsMonitor<ProcessMonitorOptions> optionsMonitor) : BackgroundService
{
// 日志对象
private readonly ILogger<Worker> _logger = logger;
// 配置文件对象
private readonly IOptionsMonitor<ProcessMonitorOptions> _optionsMonitor = optionsMonitor;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
。。。省略。。。
}
}
四. 发布
4.1 修改.csproj配置文件
🔷配置要发布相关的设置
xml
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<!-- 依赖的框架 -->
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<!-- 关闭自动Using, Using时需要手动导入 -->
<ImplicitUsings>disable</ImplicitUsings>
<!-- 自动生成的 -->
<UserSecretsId>dotnet-MyMonitorService-e4e75285-dee7-4046-82ba-a35bbe1823bd</UserSecretsId>
<!-- 不弹黑框控制台, 适合后台服务 -->
<OutputType>WinExe</OutputType>
<!-- 不输出调试信息 -->
<DebugType>none</DebugType>
<!-- 元数据 -->
<ApplicationIcon>assets\app.ico</ApplicationIcon>
<AssemblyTitle>My Monitor Service</AssemblyTitle>
<Description>自动监控并关闭指定进程的后台服务</Description>
<Company>XXX公司</Company>
<Product>监视线程</Product>
<!-- 版本 -->
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<InformationalVersion>1.0.0</InformationalVersion>
</PropertyGroup>
<ItemGroup>
<!-- 依赖的包 -->
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.5" />
<!-- 不发布开发环境配置 -->
<Content Update="appsettings.Development.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
</Content>
</ItemGroup>
</Project>
4.2 发布

-r win-x64:指定运行平台--self-contained true:不依赖机器安装.NET-p:PublishTrimmed=true:裁剪(Trim)未使用的依赖,有助于减少体积-p:TrimMode=partial:部分裁减模式,可以防止裁减过度导致缺少依赖报错-p:PublishSingleFile=true:单文件版本
csharp
dotnet publish -c Release -r win-x64 --self-contained true -o ./publish
csharp
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishTrimmed=true -p:TrimMode=partial -o ./publish
csharp
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishTrimmed=true -p:TrimMode=partial -p:PublishSingleFile=true -o ./publish
csharp
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true -o ./publish