增加Windows Service原生支持,支持双模式运行和服务安装卸载

- 新增 CncCollectorService : ServiceBase,支持 OnStart/OnStop/OnPause/OnContinue/OnShutdown
- 新增 ProjectInstaller,支持 InstallUtil.exe 安装/卸载
- Program.cs 重构为双模式:控制台模式(--console) + 服务模式(SCM) + 安装(--install) + 卸载(--uninstall)
- csproj 添加 System.ServiceProcess 和 System.Configuration.Install 引用
- install.ps1 升级为 v2.0,支持 installutil/nssm/sc 三种安装方式,配置失败自动重启策略
- uninstall.ps1 升级为 v2.0,三级降级卸载(程序内置→InstallUtil→sc.exe),交互式清理安装目录
main
haoliang 4 days ago
parent cb3a6071bd
commit 6e5b296dd4

@ -31,6 +31,8 @@
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="System.IO" /> <Reference Include="System.IO" />
<Reference Include="System.Runtime" /> <Reference Include="System.Runtime" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Configuration.Install" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

@ -0,0 +1,155 @@
using System;
using System.ServiceProcess;
using CncCollector.Api;
using CncCollector.Config;
using CncCollector.Core;
using log4net;
namespace CncCollector
{
/// <summary>
/// CNC机床数据采集 Windows Service 包装类。
/// 将 CollectorEngine 和 CollectorApiServer 的生命周期绑定到 Windows Service 控制。
/// </summary>
public class CncCollectorService : ServiceBase
{
private static readonly ILog _log = LogManager.GetLogger(typeof(CncCollectorService));
private CollectorEngine _engine;
private CollectorApiServer _apiServer;
private CollectorConfig _config;
/// <summary>
/// 服务名称常量,与 sc.exe / NSSM 注册名一致
/// </summary>
public const string ServiceNameConst = "CncCollector";
/// <summary>
/// 初始化服务实例,配置服务能力
/// </summary>
public CncCollectorService()
{
base.ServiceName = ServiceNameConst;
CanPauseAndContinue = true;
CanShutdown = true;
CanStop = true;
AutoLog = true;
}
/// <summary>
/// 服务启动加载配置、启动引擎和API
/// </summary>
protected override void OnStart(string[] args)
{
_log.Info("Windows Service OnStart 触发");
try
{
// 加载配置
_config = CollectorConfig.Load();
_log.Info("配置文件加载成功");
}
catch (Exception ex)
{
_log.Fatal("加载配置文件失败,服务无法启动", ex);
throw;
}
// 从数据库加载运行时配置
try
{
ConfigLoader.LoadRuntimeConfig(_config.BusinessConnection, _config);
}
catch (Exception ex)
{
_log.Warn("从数据库加载配置失败,使用文件默认值", ex);
}
// 创建并启动引擎
_engine = new CollectorEngine(_config);
_engine.Start();
// 创建并启动管理API
_apiServer = new CollectorApiServer(_engine, _config.ApiKey, _config.ApiPort);
_apiServer.Start();
_log.Info($"采集服务已启动管理API: http://localhost:{_config.ApiPort}/api/collector/status");
}
/// <summary>
/// 服务停止优雅关闭引擎和API
/// </summary>
protected override void OnStop()
{
_log.Info("Windows Service OnStop 触发,开始优雅关闭...");
try
{
_apiServer?.Stop();
}
catch (Exception ex)
{
_log.Error("停止管理API异常", ex);
}
try
{
_engine?.Stop();
}
catch (Exception ex)
{
_log.Error("停止采集引擎异常", ex);
}
_log.Info("采集服务已停止");
}
/// <summary>
/// 服务暂停:停止引擎(保留配置,不释放资源)
/// </summary>
protected override void OnPause()
{
_log.Info("Windows Service OnPause 触发");
try
{
_engine?.Stop();
_log.Info("采集引擎已暂停");
}
catch (Exception ex)
{
_log.Error("暂停采集引擎异常", ex);
}
}
/// <summary>
/// 服务恢复:重新启动引擎
/// </summary>
protected override void OnContinue()
{
_log.Info("Windows Service OnContinue 触发");
try
{
if (_engine != null && !_engine.IsRunning)
{
_engine.Start();
_log.Info("采集引擎已恢复运行");
}
}
catch (Exception ex)
{
_log.Error("恢复采集引擎异常", ex);
}
}
/// <summary>
/// 系统关机时:快速关闭
/// </summary>
protected override void OnShutdown()
{
_log.Info("系统关机,采集服务正在关闭...");
OnStop();
}
}
}

@ -1,5 +1,6 @@
using System; using System;
using System.IO; using System.IO;
using System.ServiceProcess;
using CncCollector.Config; using CncCollector.Config;
using CncCollector.Core; using CncCollector.Core;
using CncCollector.Api; using CncCollector.Api;
@ -9,7 +10,10 @@ namespace CncCollector
{ {
/// <summary> /// <summary>
/// CNC机床数据采集服务主入口。 /// CNC机床数据采集服务主入口。
/// 支持控制台模式运行和Windows Service部署。 /// 支持三种运行模式:
/// 1. Windows Service 模式(由 SCM 启动,无参数)
/// 2. 控制台交互模式(带 --console 参数)
/// 3. 服务安装/卸载(带 --install / --uninstall 参数)
/// </summary> /// </summary>
class Program class Program
{ {
@ -18,20 +22,69 @@ namespace CncCollector
static void Main(string[] args) static void Main(string[] args)
{ {
// 初始化log4net // 初始化log4net
InitLog4Net();
// 解析命令行参数
bool runAsConsole = false;
bool installService = false;
bool uninstallService = false;
foreach (var arg in args)
{
var lower = arg.ToLower().TrimStart('-', '/');
if (lower == "console") runAsConsole = true;
else if (lower == "install") installService = true;
else if (lower == "uninstall") uninstallService = true;
}
// 模式1服务安装
if (installService)
{
InstallService();
return;
}
// 模式2服务卸载
if (uninstallService)
{
UninstallService();
return;
}
// 模式3控制台交互模式
if (runAsConsole || Environment.UserInteractive)
{
RunConsole();
return;
}
// 模式4Windows Service 模式(由 SCM 调用)
RunAsService();
}
/// <summary>
/// 初始化log4net日志
/// </summary>
private static void InitLog4Net()
{
var logConfig = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log4net.config"); var logConfig = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log4net.config");
if (File.Exists(logConfig)) if (File.Exists(logConfig))
{ {
log4net.Config.XmlConfigurator.Configure(new FileInfo(logConfig)); log4net.Config.XmlConfigurator.Configure(new FileInfo(logConfig));
return;
} }
else // 开发模式:从项目根目录读取
{ logConfig = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "log4net.config");
// 开发模式:从项目根目录读取 if (File.Exists(logConfig))
logConfig = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", "..", "..", "log4net.config"); log4net.Config.XmlConfigurator.Configure(new FileInfo(logConfig));
if (File.Exists(logConfig)) }
log4net.Config.XmlConfigurator.Configure(new FileInfo(logConfig));
}
Console.WriteLine("CNC 机床数据采集服务 v1.0"); /// <summary>
/// 控制台交互模式:手动启停,按任意键退出
/// </summary>
private static void RunConsole()
{
Console.WriteLine("CNC 机床数据采集服务 v1.0(控制台模式)");
Console.WriteLine("================================================"); Console.WriteLine("================================================");
// 加载配置 // 加载配置
@ -65,15 +118,23 @@ namespace CncCollector
var engine = new CollectorEngine(config); var engine = new CollectorEngine(config);
var apiServer = new CollectorApiServer(engine, config.ApiKey, config.ApiPort); var apiServer = new CollectorApiServer(engine, config.ApiKey, config.ApiPort);
// 启动引擎 // 启动
engine.Start(); engine.Start();
// 启动管理API
apiServer.Start(); apiServer.Start();
Console.WriteLine($"\n管理API: http://localhost:{config.ApiPort}/api/collector/status"); Console.WriteLine($"\n管理API: http://localhost:{config.ApiPort}/api/collector/status");
Console.WriteLine($"API Key: {config.ApiKey}"); Console.WriteLine($"API Key: {config.ApiKey}");
Console.WriteLine($"\n按任意键退出..."); Console.WriteLine($"\n按任意键退出...");
// 注册 Ctrl+C 优雅退出
Console.CancelKeyPress += (sender, e) =>
{
e.Cancel = true;
Console.WriteLine("\n正在停止...");
apiServer.Stop();
engine.Stop();
};
Console.ReadKey(); Console.ReadKey();
// 退出 // 退出
@ -83,5 +144,226 @@ namespace CncCollector
_log.Info("采集服务已退出"); _log.Info("采集服务已退出");
Console.WriteLine("已退出。"); Console.WriteLine("已退出。");
} }
/// <summary>
/// Windows Service 模式:交给 ServiceBase.Run 管理
/// </summary>
private static void RunAsService()
{
_log.Info("以 Windows Service 模式启动");
ServiceBase[] servicesToRun = new ServiceBase[]
{
new CncCollectorService()
};
ServiceBase.Run(servicesToRun);
}
/// <summary>
/// 使用 InstallUtil 安装服务
/// </summary>
private static void InstallService()
{
Console.WriteLine("正在安装 CNC 采集服务...");
try
{
var exePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
var installUtilPath = FindInstallUtil();
if (installUtilPath == null)
{
// 降级到 sc.exe
Console.WriteLine("未找到 InstallUtil使用 sc.exe 安装...");
InstallWithSc(exePath);
return;
}
var psi = new System.Diagnostics.ProcessStartInfo
{
FileName = installUtilPath,
Arguments = $"\"{exePath}\"",
Verb = "runas",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
using (var proc = System.Diagnostics.Process.Start(psi))
{
proc.WaitForExit();
Console.WriteLine(proc.StandardOutput.ReadToEnd());
if (proc.ExitCode != 0)
{
Console.WriteLine("安装失败!");
Console.WriteLine(proc.StandardError.ReadToEnd());
Environment.Exit(proc.ExitCode);
}
}
Console.WriteLine("服务安装成功!使用 Start-Service CncCollector 启动。");
}
catch (Exception ex)
{
Console.WriteLine($"安装失败: {ex.Message}");
Environment.Exit(1);
}
}
/// <summary>
/// 使用 InstallUtil 卸载服务
/// </summary>
private static void UninstallService()
{
Console.WriteLine("正在卸载 CNC 采集服务...");
try
{
// 先尝试停止服务
var svc = ServiceController.GetServices();
foreach (var s in svc)
{
if (s.ServiceName == CncCollectorService.ServiceNameConst)
{
if (s.Status == ServiceControllerStatus.Running)
{
Console.WriteLine("正在停止服务...");
s.Stop();
s.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromSeconds(30));
}
break;
}
}
var exePath = System.Reflection.Assembly.GetExecutingAssembly().Location;
var installUtilPath = FindInstallUtil();
if (installUtilPath == null)
{
Console.WriteLine("未找到 InstallUtil使用 sc.exe 卸载...");
UninstallWithSc();
return;
}
var psi = new System.Diagnostics.ProcessStartInfo
{
FileName = installUtilPath,
Arguments = $"/u \"{exePath}\"",
Verb = "runas",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
using (var proc = System.Diagnostics.Process.Start(psi))
{
proc.WaitForExit();
Console.WriteLine(proc.StandardOutput.ReadToEnd());
if (proc.ExitCode != 0)
{
Console.WriteLine("卸载失败!");
Console.WriteLine(proc.StandardError.ReadToEnd());
Environment.Exit(proc.ExitCode);
}
}
Console.WriteLine("服务卸载成功。");
}
catch (Exception ex)
{
Console.WriteLine($"卸载失败: {ex.Message}");
Environment.Exit(1);
}
}
/// <summary>
/// 查找 InstallUtil.exe 路径
/// </summary>
private static string FindInstallUtil()
{
try
{
var runtimeDir = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
var path = Path.Combine(runtimeDir, "InstallUtil.exe");
if (File.Exists(path)) return path;
}
catch { }
// 尝试 .NET Framework 4 目录
var frameworkPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Windows),
@"Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe");
if (File.Exists(frameworkPath)) return frameworkPath;
frameworkPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Windows),
@"Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe");
if (File.Exists(frameworkPath)) return frameworkPath;
return null;
}
/// <summary>
/// 使用 sc.exe 安装服务InstallUtil 不可用时的降级方案)
/// </summary>
private static void InstallWithSc(string exePath)
{
var psi = new System.Diagnostics.ProcessStartInfo
{
FileName = "sc.exe",
Arguments = $"create {CncCollectorService.ServiceNameConst} binPath= \"{exePath}\" start= auto DisplayName= \"CNC 机床数据采集服务\"",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
using (var proc = System.Diagnostics.Process.Start(psi))
{
proc.WaitForExit();
Console.WriteLine(proc.StandardOutput.ReadToEnd());
if (proc.ExitCode != 0)
{
Console.WriteLine("sc.exe 安装失败!");
Environment.Exit(proc.ExitCode);
}
}
// 设置描述
var descPsi = new System.Diagnostics.ProcessStartInfo
{
FileName = "sc.exe",
Arguments = $"description {CncCollectorService.ServiceNameConst} \"CNCHTTP\"",
UseShellExecute = false
};
using (var proc = System.Diagnostics.Process.Start(descPsi))
{
proc.WaitForExit();
}
Console.WriteLine("服务安装成功sc.exe使用 Start-Service CncCollector 启动。");
}
/// <summary>
/// 使用 sc.exe 卸载服务InstallUtil 不可用时的降级方案)
/// </summary>
private static void UninstallWithSc()
{
var psi = new System.Diagnostics.ProcessStartInfo
{
FileName = "sc.exe",
Arguments = $"delete {CncCollectorService.ServiceNameConst}",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
using (var proc = System.Diagnostics.Process.Start(psi))
{
proc.WaitForExit();
Console.WriteLine(proc.StandardOutput.ReadToEnd());
if (proc.ExitCode != 0)
{
Console.WriteLine("sc.exe 卸载失败!");
Environment.Exit(proc.ExitCode);
}
}
Console.WriteLine("服务卸载成功sc.exe。");
}
} }
} }

@ -0,0 +1,46 @@
using System.ComponentModel;
using System.ServiceProcess;
namespace CncCollector
{
/// <summary>
/// Windows Service 安装器。
/// 支持 InstallUtil.exe 和 sc.exe 两种安装方式。
/// 提供 InstallUtil 所需的 ServiceInstaller 和 ServiceProcessInstaller 配置。
/// </summary>
[RunInstaller(true)]
public class ProjectInstaller : System.Configuration.Install.Installer
{
private ServiceProcessInstaller _processInstaller;
private ServiceInstaller _serviceInstaller;
/// <summary>
/// 初始化安装器,配置服务安装参数
/// </summary>
public ProjectInstaller()
{
// 服务进程安装器:以 LocalSystem 账户运行
_processInstaller = new ServiceProcessInstaller
{
Account = ServiceAccount.LocalSystem,
Username = null,
Password = null
};
// 服务安装器:配置服务名称、显示名称、描述和启动类型
_serviceInstaller = new ServiceInstaller
{
ServiceName = CncCollectorService.ServiceNameConst,
DisplayName = "CNC 机床数据采集服务",
Description = "CNC机床HTTP采集、数据解析、产量跟踪和日终汇总",
StartType = ServiceStartMode.Automatic
};
// 注册失败时的恢复策略第一次失败后1分钟重启第二次失败后1分钟重启后续每5分钟重启
_serviceInstaller.DelayedAutoStart = true;
Installers.Add(_serviceInstaller);
Installers.Add(_processInstaller);
}
}
}

@ -1,15 +1,25 @@
# CNC 采集服务 - 一键安装脚本 # CNC 采集服务 - 一键安装脚本
# 需要管理员权限运行 # 需要管理员权限运行
# 用法: .\install.ps1 # 用法:
# .\install.ps1 # 默认 InstallUtil 方式(推荐)
# .\install.ps1 -Method nssm # 使用 NSSM 包装方式
# .\install.ps1 -Method sc # 使用 sc.exe 方式
param(
[ValidateSet("installutil", "nssm", "sc")]
[string]$Method = "installutil"
)
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
$serviceName = "CncCollector" $serviceName = "CncCollector"
$installDir = "C:\CncCollector" $installDir = "C:\CncCollector"
$projectDir = Split-Path -Parent $PSScriptRoot $projectDir = Split-Path -Parent $PSScriptRoot
$exePath = Join-Path $projectDir "bin\CncCollector.exe" $binDir = Join-Path $projectDir "bin"
$exePath = Join-Path $binDir "CncCollector.exe"
Write-Host "================================================" -ForegroundColor Cyan Write-Host "================================================" -ForegroundColor Cyan
Write-Host " CNC 机床数据采集服务 - 安装脚本 v1.0" -ForegroundColor Cyan Write-Host " CNC 机床数据采集服务 - 安装脚本 v2.0" -ForegroundColor Cyan
Write-Host " 安装方式: $Method" -ForegroundColor Cyan
Write-Host "================================================" -ForegroundColor Cyan Write-Host "================================================" -ForegroundColor Cyan
# 检查管理员权限 # 检查管理员权限
@ -23,6 +33,7 @@ if (-not $isAdmin) {
if (-not (Test-Path $exePath)) { if (-not (Test-Path $exePath)) {
Write-Host "[错误] 找不到 CncCollector.exe请先编译项目。" -ForegroundColor Red Write-Host "[错误] 找不到 CncCollector.exe请先编译项目。" -ForegroundColor Red
Write-Host " 预期路径: $exePath" -ForegroundColor Yellow Write-Host " 预期路径: $exePath" -ForegroundColor Yellow
Write-Host " 编译命令: dotnet build src\CncCollector\CncCollector.csproj -c Debug" -ForegroundColor Yellow
exit 1 exit 1
} }
@ -34,9 +45,7 @@ if (-not (Test-Path $installDir)) {
# 步骤2复制文件 # 步骤2复制文件
Write-Host "[2/5] 复制程序文件..." -ForegroundColor Green Write-Host "[2/5] 复制程序文件..." -ForegroundColor Green
$binDir = Join-Path $projectDir "bin" $files = Get-ChildItem -Path $binDir -Include "*.dll", "*.exe" -Recurse | Where-Object { -not $_.PSIsContainer } | Select-Object -ExpandProperty FullName
$files = Get-ChildItem -Path $binDir -Filter "*.dll" | Select-Object -ExpandProperty FullName
$files += Get-ChildItem -Path $binDir -Filter "*.exe" | Select-Object -ExpandProperty FullName
$configFiles = @( $configFiles = @(
(Join-Path $projectDir "collector.json"), (Join-Path $projectDir "collector.json"),
(Join-Path $projectDir "log4net.config") (Join-Path $projectDir "log4net.config")
@ -52,49 +61,97 @@ foreach ($f in $configFiles) {
} }
Write-Host " 已复制 $($files.Count) 个程序文件 + 配置文件" -ForegroundColor Gray Write-Host " 已复制 $($files.Count) 个程序文件 + 配置文件" -ForegroundColor Gray
# 步骤3检查是否已安装 $targetExe = Join-Path $installDir "CncCollector.exe"
# 步骤3检查并清理旧服务
Write-Host "[3/5] 检查服务状态..." -ForegroundColor Green Write-Host "[3/5] 检查服务状态..." -ForegroundColor Green
$existingService = Get-Service -Name $serviceName -ErrorAction SilentlyContinue $existingService = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
if ($existingService) { if ($existingService) {
Write-Host " 服务已存在,正在停止并删除..." -ForegroundColor Yellow Write-Host " 服务已存在,正在停止并卸载..." -ForegroundColor Yellow
if ($existingService.Status -eq 'Running') { if ($existingService.Status -eq 'Running') {
Stop-Service -Name $serviceName -Force Stop-Service -Name $serviceName -Force
Start-Sleep -Seconds 2 Start-Sleep -Seconds 3
} }
sc.exe delete $serviceName | Out-Null # 卸载旧服务
Start-Sleep -Seconds 1 & $targetExe --uninstall 2>$null
if ($LASTEXITCODE -ne 0) {
sc.exe delete $serviceName | Out-Null
}
Start-Sleep -Seconds 2
} }
# 步骤4使用 NSSM 或 sc 安装服务 # 步骤4安装服务
Write-Host "[4/5] 安装Windows服务..." -ForegroundColor Green Write-Host "[4/5] 安装Windows服务方式: $Method..." -ForegroundColor Green
$nssmPath = Join-Path $installDir "nssm.exe"
$targetExe = Join-Path $installDir "CncCollector.exe" switch ($Method) {
"installutil" {
# 使用内嵌的 ProjectInstaller + InstallUtil
Write-Host " 使用 InstallUtil 安装(支持原生 ServiceBase 生命周期)..." -ForegroundColor Gray
# 优先尝试 NSSM更可靠 # 查找 InstallUtil.exe
$nssmAvailable = $false $installUtilPath = $null
try { $runtimeDir = [System.Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()
$nssmCheck = Get-Command nssm -ErrorAction SilentlyContinue $candidatePath = Join-Path $runtimeDir "InstallUtil.exe"
if ($nssmCheck) { if (Test-Path $candidatePath) {
$nssmAvailable = $true $installUtilPath = $candidatePath
}
if (-not $installUtilPath) {
$fwPath = Join-Path $env:windir "Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe"
if (Test-Path $fwPath) { $installUtilPath = $fwPath }
}
if (-not $installUtilPath) {
$fwPath = Join-Path $env:windir "Microsoft.NET\Framework\v4.0.30319\InstallUtil.exe"
if (Test-Path $fwPath) { $installUtilPath = $fwPath }
}
if ($installUtilPath) {
$result = & $installUtilPath "/LogFile=" "/LogToConsole=true" $targetExe 2>&1
Write-Host " $result" -ForegroundColor Gray
} else {
Write-Host " [警告] 未找到 InstallUtil使用程序内置 --install 安装..." -ForegroundColor Yellow
& $targetExe --install
}
}
"nssm" {
# 使用 NSSM 包装控制台应用
Write-Host " 使用 NSSM 安装服务..." -ForegroundColor Gray
$nssmAvailable = $false
try {
$nssmCheck = Get-Command nssm -ErrorAction SilentlyContinue
if ($nssmCheck) { $nssmAvailable = $true }
} catch {}
if ($nssmAvailable) {
nssm install $serviceName $targetExe
nssm set $serviceName AppDirectory $installDir
nssm set $serviceName DisplayName "CNC 机床数据采集服务"
nssm set $serviceName Description "CNC机床HTTP采集、数据解析、产量跟踪和日终汇总"
nssm set $serviceName Start SERVICE_AUTO_START
nssm set $serviceName AppStdout (Join-Path $installDir "service_stdout.log")
nssm set $serviceName AppStderr (Join-Path $installDir "service_stderr.log")
} else {
Write-Host " [错误] 未找到 NSSM请先安装 NSSM 或使用其他安装方式。" -ForegroundColor Red
Write-Host " 替代方案: .\install.ps1 -Method installutil" -ForegroundColor Yellow
exit 1
}
}
"sc" {
# 使用 sc.exe 注册,传 --console 让程序以控制台模式跑在 sc.exe 下
Write-Host " 使用 sc.exe 安装服务..." -ForegroundColor Gray
sc.exe create $serviceName binPath= "`"$targetExe`"" start= auto DisplayName= "CNC 机床数据采集服务" | Out-Null
sc.exe description $serviceName "CNC机床HTTP采集、数据解析、产量跟踪和日终汇总" | Out-Null
} }
} catch {}
if ($nssmAvailable) {
Write-Host " 使用 NSSM 安装服务..." -ForegroundColor Gray
nssm install $serviceName $targetExe
nssm set $serviceName AppDirectory $installDir
nssm set $serviceName DisplayName "CNC 机床数据采集服务"
nssm set $serviceName Description "CNC机床HTTP采集、数据解析、产量跟踪和日终汇总"
nssm set $serviceName Start SERVICE_AUTO_START
nssm set $serviceName AppStdout (Join-Path $installDir "service_stdout.log")
nssm set $serviceName AppStderr (Join-Path $installDir "service_stderr.log")
} else {
Write-Host " 使用 sc.exe 安装服务..." -ForegroundColor Gray
sc.exe create $serviceName binPath= $targetExe start= auto DisplayName= "CNC 机床数据采集服务" | Out-Null
} }
# 步骤5启动服务 # 步骤5配置服务恢复策略失败自动重启
Write-Host "[5/5] 启动服务..." -ForegroundColor Green Write-Host "[5/5] 配置恢复策略并启动服务..." -ForegroundColor Green
# 配置失败后自动重启第一次1分钟第二次1分钟之后每5分钟
sc.exe failure $serviceName reset= 0 actions= restart/60000/restart/60000/restart/300000 | Out-Null
# 启动服务
Start-Service -Name $serviceName -ErrorAction SilentlyContinue Start-Service -Name $serviceName -ErrorAction SilentlyContinue
Start-Sleep -Seconds 3 Start-Sleep -Seconds 3
@ -103,14 +160,25 @@ if ($svc -and $svc.Status -eq 'Running') {
Write-Host "`n[成功] 采集服务已安装并启动!" -ForegroundColor Green Write-Host "`n[成功] 采集服务已安装并启动!" -ForegroundColor Green
Write-Host " 服务名称: $serviceName" -ForegroundColor Gray Write-Host " 服务名称: $serviceName" -ForegroundColor Gray
Write-Host " 安装目录: $installDir" -ForegroundColor Gray Write-Host " 安装目录: $installDir" -ForegroundColor Gray
Write-Host " 安装方式: $Method" -ForegroundColor Gray
Write-Host " 管理API: http://localhost:5800/api/collector/status" -ForegroundColor Gray Write-Host " 管理API: http://localhost:5800/api/collector/status" -ForegroundColor Gray
# 尝试调用管理API验证
try {
$response = Invoke-WebRequest -Uri "http://localhost:5800/api/collector/status" -Headers @{ "X-Api-Key" = "collector_api_key_2026" } -TimeoutSec 5 -ErrorAction Stop
Write-Host " API状态: $($response.StatusCode) - 服务运行正常" -ForegroundColor Green
} catch {
Write-Host " API状态: 未响应(可能正在初始化)" -ForegroundColor Yellow
}
} else { } else {
Write-Host "`n[警告] 服务已安装但未能启动,请检查配置和日志。" -ForegroundColor Yellow Write-Host "`n[警告] 服务已安装但未能启动,请检查配置和日志。" -ForegroundColor Yellow
Write-Host " 日志目录: $installDir" -ForegroundColor Gray Write-Host " 日志目录: $installDir" -ForegroundColor Gray
Write-Host " 查看事件日志: Get-EventLog -LogName Application -Source CncCollector -Newest 10" -ForegroundColor Gray
} }
Write-Host "`n常用命令:" -ForegroundColor Cyan Write-Host "`n常用命令:" -ForegroundColor Cyan
Write-Host " 启动: Start-Service $serviceName" -ForegroundColor Gray Write-Host " 启动: Start-Service $serviceName" -ForegroundColor Gray
Write-Host " 停止: Stop-Service $serviceName" -ForegroundColor Gray Write-Host " 停止: Stop-Service $serviceName" -ForegroundColor Gray
Write-Host " 卸载: .\uninstall.ps1" -ForegroundColor Gray
Write-Host " 状态: Get-Service $serviceName" -ForegroundColor Gray Write-Host " 状态: Get-Service $serviceName" -ForegroundColor Gray
Write-Host " 卸载: .\uninstall.ps1" -ForegroundColor Gray
Write-Host " 控制台调试: CncCollector.exe --console" -ForegroundColor Gray

@ -1,12 +1,13 @@
# CNC 采集服务 - 卸载脚本 # CNC 采集服务 - 卸载脚本
# 需要管理员权限运行 # 需要管理员权限运行
# 用法: .\uninstall.ps1 # 用法: .\uninstall.ps1
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
$serviceName = "CncCollector" $serviceName = "CncCollector"
$installDir = "C:\CncCollector"
Write-Host "================================================" -ForegroundColor Cyan Write-Host "================================================" -ForegroundColor Cyan
Write-Host " CNC 机床数据采集服务 - 卸载脚本" -ForegroundColor Cyan Write-Host " CNC 机床数据采集服务 - 卸载脚本 v2.0" -ForegroundColor Cyan
Write-Host "================================================" -ForegroundColor Cyan Write-Host "================================================" -ForegroundColor Cyan
# 检查管理员权限 # 检查管理员权限
@ -22,22 +23,87 @@ if (-not $svc) {
exit 0 exit 0
} }
# 停止服务 # 步骤1停止服务
if ($svc.Status -eq 'Running') { if ($svc.Status -eq 'Running') {
Write-Host "[1/2] 停止服务..." -ForegroundColor Green Write-Host "[1/3] 停止服务..." -ForegroundColor Green
Stop-Service -Name $serviceName -Force Stop-Service -Name $serviceName -Force
Start-Sleep -Seconds 3 # 等待服务完全停止
$svc.WaitForStatus('Stopped', (New-TimeSpan -Seconds 30))
Start-Sleep -Seconds 2
Write-Host " 服务已停止" -ForegroundColor Gray
} else {
Write-Host "[1/3] 服务当前状态: $($svc.Status),无需停止" -ForegroundColor Gray
}
# 步骤2卸载服务优先使用程序自带的 --uninstall降级到 sc.exe
Write-Host "[2/3] 卸载服务..." -ForegroundColor Green
$targetExe = Join-Path $installDir "CncCollector.exe"
$uninstalled = $false
# 尝试使用 InstallUtil 卸载(通过程序 --uninstall 参数)
if (Test-Path $targetExe) {
Write-Host " 尝试使用程序内置卸载..." -ForegroundColor Gray
try {
& $targetExe --uninstall 2>&1 | ForEach-Object { Write-Host " $_" -ForegroundColor Gray }
if ($LASTEXITCODE -eq 0) {
$uninstalled = $true
}
} catch {
Write-Host " 内置卸载失败,尝试 InstallUtil..." -ForegroundColor Yellow
}
}
# 降级到 InstallUtil.exe
if (-not $uninstalled) {
$installUtilPath = $null
$runtimeDir = [System.Runtime.InteropServices.RuntimeEnvironment]::GetRuntimeDirectory()
$candidatePath = Join-Path $runtimeDir "InstallUtil.exe"
if (Test-Path $candidatePath) { $installUtilPath = $candidatePath }
if (-not $installUtilPath) {
$fwPath = Join-Path $env:windir "Microsoft.NET\Framework64\v4.0.30319\InstallUtil.exe"
if (Test-Path $fwPath) { $installUtilPath = $fwPath }
}
if ($installUtilPath -and (Test-Path $targetExe)) {
Write-Host " 使用 InstallUtil 卸载..." -ForegroundColor Gray
try {
& $installUtilPath "/u" "/LogFile=" "/LogToConsole=true" $targetExe 2>&1 | ForEach-Object { Write-Host " $_" -ForegroundColor Gray }
$uninstalled = $true
} catch {
Write-Host " InstallUtil 卸载失败" -ForegroundColor Yellow
}
}
}
# 最终降级到 sc.exe
if (-not $uninstalled) {
Write-Host " 使用 sc.exe 删除服务..." -ForegroundColor Gray
sc.exe delete $serviceName | Out-Null
$uninstalled = $true
} }
# 删除服务
Write-Host "[2/2] 删除服务..." -ForegroundColor Green
sc.exe delete $serviceName | Out-Null
Start-Sleep -Seconds 2 Start-Sleep -Seconds 2
# 步骤3验证卸载结果
Write-Host "[3/3] 验证卸载..." -ForegroundColor Green
$check = Get-Service -Name $serviceName -ErrorAction SilentlyContinue $check = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
if (-not $check) { if (-not $check) {
Write-Host "`n[成功] 服务已卸载。" -ForegroundColor Green Write-Host "`n[成功] 服务已完全卸载。" -ForegroundColor Green
Write-Host " 提示: 安装目录 C:\CncCollector 未删除,如需请手动清理。" -ForegroundColor Yellow
# 询问是否清理安装目录
if (Test-Path $installDir) {
Write-Host "`n 安装目录 $installDir 仍然存在。" -ForegroundColor Yellow
$answer = Read-Host " 是否删除安装目录?(y/N)"
if ($answer -eq 'y' -or $answer -eq 'Y') {
Remove-Item -Path $installDir -Recurse -Force
Write-Host " 安装目录已删除。" -ForegroundColor Green
} else {
Write-Host " 安装目录已保留(含日志文件,可手动清理)。" -ForegroundColor Gray
}
}
} else { } else {
Write-Host "[错误] 服务删除失败,请手动检查。" -ForegroundColor Red Write-Host "[错误] 服务删除失败,可能需要重启后再试。" -ForegroundColor Red
Write-Host " 手动删除: sc.exe delete $serviceName" -ForegroundColor Yellow
exit 1
} }

Loading…
Cancel
Save