第一章:golang注册为windows服务
在 Windows 平台上将 Go 程序作为系统服务运行,可实现后台长期驻留、开机自启、无用户会话依赖等关键能力。Go 本身不内置 Windows 服务支持,需借助 golang.org/x/sys/windows/svc 官方扩展包与 Windows 服务控制管理器(SCM)交互。
服务程序结构设计
核心需实现 svc.Handler 接口的 Execute 方法,处理 Start、Stop、Pause 等 SCM 指令。主函数中调用 svc.Run("YourServiceName", &program{}) 启动服务上下文,并通过 syscall.SetConsoleCtrlHandler 兼容调试模式(控制台直接运行时响应 Ctrl+C)。
编译与安装步骤
使用 GOOS=windows GOARCH=amd64 go build -o myservice.exe main.go 编译为 Windows 可执行文件。安装服务需管理员权限,执行以下命令:
sc create "MyGoService" binPath= "C:\path\to\myservice.exe" start= auto obj= "LocalSystem" DisplayName= "My Go Application Service"
注意:
binPath=后必须有空格,start=支持auto/demand/disabled;obj=指定运行账户,LocalSystem权限最高,生产环境建议使用专用低权限服务账户。
服务生命周期控制
常用 SCM 命令如下:
| 操作 | 命令示例 |
|---|---|
| 启动服务 | sc start "MyGoService" |
| 停止服务 | sc stop "MyGoService" |
| 查看状态 | sc query "MyGoService" |
| 卸载服务 | sc delete "MyGoService" |
日志与调试建议
Windows 服务无法直接输出到控制台,推荐使用 eventlog 包写入 Windows 事件查看器:
import "golang.org/x/sys/windows/svc/eventlog"
// 初始化:eventlog.Install("MyGoService", "Application")
// 记录错误:el.Error(1, "Failed to bind port: %v", err)
调试阶段可添加命令行参数(如 -debug),使程序跳过 svc.Run 直接以控制台模式运行,便于日志观察与断点调试。
第二章:Windows服务基础与Go语言适配原理
2.1 Windows服务生命周期与SCM交互机制
Windows服务并非独立运行进程,而是由服务控制管理器(SCM)统一调度的可执行实体。其生命周期严格遵循 SCM 的状态机模型。
核心状态流转
SCM 通过 ControlService 和 StartService 等 API 驱动服务状态迁移:
SERVICE_STOPPED→SERVICE_START_PENDING→SERVICE_RUNNINGSERVICE_RUNNING→SERVICE_STOP_PENDING→SERVICE_STOPPED
// 启动服务示例(需管理员权限)
SC_HANDLE schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
SC_HANDLE schService = OpenService(schSCManager, L"MyService", SERVICE_START);
StartService(schService, 0, NULL); // dwNumServiceArgs=0, lpServiceArgVectors=NULL
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
StartService 调用后,SCM 创建服务进程并调用其 ServiceMain 入口;dwNumServiceArgs 指定启动参数数量,NULL 表示无额外参数。
SCM 与服务通信通道
| 组件 | 作用 |
|---|---|
| SCM | 运行于 services.exe,本地系统级服务管理器 |
| 服务进程 | 实现 HandlerEx 回调响应控制请求 |
| RPC端点 | SCM 通过 \\.\pipe\ntsvcs 与服务进程安全通信 |
graph TD
A[SCM: StartService] --> B[创建服务进程]
B --> C[调用 ServiceMain]
C --> D[注册 HandlerEx]
D --> E[接收 SCM 控制指令]
E --> F[执行自定义逻辑]
2.2 Go运行时与Windows服务控制台的兼容性分析
Go 运行时在 Windows 上以 console application 模式启动,但 Windows 服务要求 SERVICE_WIN32_OWN_PROCESS 类型且禁止直接交互式控制台 I/O。
控制台句柄冲突问题
当服务通过 svchost.exe 加载时,os.Stdin/Stdout/Stderr 默认为 INVALID_HANDLE_VALUE,导致 log.Printf 或 fmt.Println 触发 panic。
// 检测当前是否以服务模式运行
func isServiceMode() bool {
h := syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE)
return h == syscall.InvalidHandle
}
逻辑说明:
GetStdHandle(STD_OUTPUT_HANDLE)在服务会话中返回InvalidHandle;该检测可避免日志写入失败。参数STD_OUTPUT_HANDLE值为-11,由 Windows API 定义。
兼容性策略对比
| 方案 | 控制台支持 | 日志可靠性 | Go GC 友好性 |
|---|---|---|---|
github.com/kardianos/service |
✅(重定向) | ✅(文件+EventLog) | ✅ |
原生 syscall.CreateService |
❌ | ⚠️(需手动重定向) | ✅ |
graph TD
A[Go主goroutine] --> B{IsService?}
B -->|Yes| C[DetachConsole<br>Redirect stdout to EventLog]
B -->|No| D[Attach to console]
2.3 syscall、golang.org/x/sys/windows包核心API实践
Windows系统调用需绕过Go运行时抽象,直接与NTDLL或Kernel32交互。golang.org/x/sys/windows 提供了安全封装,而 syscall(已弃用但仍有遗留场景)则暴露底层接口。
常用API对比
| 功能 | syscall 方式 | x/sys/windows 方式 |
|---|---|---|
| 获取进程句柄 | syscall.OpenProcess |
windows.OpenProcess |
| 内存分配 | syscall.VirtualAlloc |
windows.VirtualAlloc |
| 等待对象 | syscall.WaitForSingleObject |
windows.WaitForSingleObject |
调用示例:打开进程并读取内存
h, err := windows.OpenProcess(
windows.PROCESS_QUERY_INFORMATION|windows.PROCESS_VM_READ,
false,
uint32(pid),
)
if err != nil {
log.Fatal(err)
}
defer windows.CloseHandle(h)
逻辑分析:OpenProcess 接收三个参数——访问权限标志(按位或组合)、是否继承句柄(false表示不继承)、目标进程ID(需转为uint32)。权限标志决定后续能否调用ReadProcessMemory等操作。
数据同步机制
Windows内核对象(如Event、Mutex)支持跨进程同步,x/sys/windows 中的 CreateEvent 和 SetEvent 可无缝集成至Go channel协作模型。
2.4 服务主函数结构设计:ServiceMain与HandlerEx双模式实现
Windows 服务的核心入口由 ServiceMain 和控制处理器 HandlerEx 协同构成,二者分工明确:前者负责初始化与长期运行逻辑,后者响应 SCM(服务控制管理器)发来的控制请求。
ServiceMain:服务生命周期起点
VOID WINAPI ServiceMain(DWORD argc, LPWSTR *argv) {
SERVICE_STATUS_HANDLE hStatus = RegisterServiceCtrlHandlerEx(
SERVICE_NAME, HandlerEx, NULL); // 注册扩展处理器
if (!hStatus) return;
g_status.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus(hStatus, &g_status);
// 初始化资源、启动工作线程...
g_status.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(hStatus, &g_status);
WaitForSingleObject(g_hStopEvent, INFINITE); // 阻塞等待停止信号
}
argc/argv 为 SCM 传递的服务启动参数(常为空);RegisterServiceCtrlHandlerEx 启用 HANDLER_EX 模式,支持 SERVICE_CONTROL_SESSIONCHANGE 等高级控制码,而传统 Handler 不支持。
HandlerEx:统一控制分发中枢
DWORD WINAPI HandlerEx(DWORD dwControl, DWORD dwEventType,
LPVOID lpEventData, LPVOID lpContext) {
switch (dwControl) {
case SERVICE_CONTROL_STOP:
SetEvent(g_hStopEvent);
break;
case SERVICE_CONTROL_PAUSE:
g_status.dwCurrentState = SERVICE_PAUSED;
break;
default:
return ERROR_CALL_NOT_IMPLEMENTED;
}
SetServiceStatus(g_hStatus, &g_status);
return NO_ERROR;
}
dwEventType 和 lpEventData 在会话变更等场景中携带上下文(如 WTSSESSION_NOTIFICATION),体现双模式对现代桌面交互的适配能力。
双模式能力对比
| 能力维度 | Handler(旧) | HandlerEx(新) |
|---|---|---|
| 支持控制码 | 基础7种 | 扩展至10+种(含会话、电源事件) |
| 事件数据传递 | ❌ 无 | ✅ lpEventData 提供结构化上下文 |
| Windows Server 兼容性 | ≥2003 | ≥Vista / Server 2008 |
graph TD
A[SCM发送控制请求] --> B{HandlerEx被调用}
B --> C[解析dwControl与dwEventType]
C --> D[分发至STOP/PAUSE/SESSION_CHANGE等分支]
D --> E[更新服务状态并触发业务响应]
2.5 服务状态同步:SERVICE_STATUS与WaitForControlEvent实战封装
数据同步机制
Windows 服务需实时向 SCM(服务控制管理器)上报运行状态,SERVICE_STATUS 结构体是核心载体,其中 dwCurrentState、dwWin32ExitCode 和 dwCheckPoint 构成状态跃迁的关键信号。
核心封装实践
以下为线程安全的状态更新与控制事件等待封装:
void UpdateServiceStatus(SERVICE_STATUS_HANDLE hStatus, DWORD state, DWORD exitCode = NO_ERROR) {
SERVICE_STATUS ss = {0};
ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
ss.dwCurrentState = state;
ss.dwWin32ExitCode = exitCode;
ss.dwWaitHint = (state == SERVICE_START_PENDING) ? 5000 : 0;
ss.dwCheckPoint = (state == SERVICE_START_PENDING) ? ++g_CheckPoint : 0;
SetServiceStatus(hStatus, &ss); // 原子上报至SCM
}
逻辑分析:
dwCheckPoint仅在START_PENDING时递增,告知 SCM 启动进度;dwWaitHint指示下次状态更新的预期延迟(毫秒)。SetServiceStatus是唯一合法的 SCM 状态通道,失败将导致服务被标记为“无响应”。
控制事件响应模型
graph TD
A[WaitForControlEvent] --> B{收到 STOP?}
B -->|Yes| C[调用 UpdateServiceStatus → STOP_PENDING]
B -->|Yes| D[执行清理逻辑]
D --> E[UpdateServiceStatus → STOPPED]
关键字段对照表
| 字段 | 含义 | 典型取值 |
|---|---|---|
dwCurrentState |
当前服务生命周期状态 | SERVICE_RUNNING, SERVICE_STOP_PENDING |
dwControlsAccepted |
接受的控制码掩码 | SERVICE_ACCEPT_STOP \| SERVICE_ACCEPT_SHUTDOWN |
第三章:服务可执行体构建与安装注册
3.1 构建无控制台窗口的GUI子系统服务二进制
Windows 服务默认以 SERVICE_WIN32_OWN_PROCESS 类型运行,但若需承载 GUI 子系统(如嵌入式 WebView2 或自绘 UI 线程),必须绕过控制台窗口创建并启用交互式会话。
关键配置项
- 设置服务类型为
SERVICE_INTERACTIVE_PROCESS(仅限旧版系统,现代方案推荐WTSRegisterSessionNotification+ 会话切换监听) - 调用
SetThreadDesktop(OpenInputDesktop())切换到用户桌面 - 使用
CreateWindowEx(..., WS_EX_NOACTIVATE | WS_EX_TOOLWINDOW)隐藏主窗口边框与任务栏图标
示例:服务主循环中初始化 GUI 上下文
// 在 ServiceMain 中调用
HDESK hDesk = OpenInputDesktop(0, FALSE, GENERIC_ALL);
if (hDesk) {
SetThreadDesktop(hDesk); // 关键:将当前线程绑定至交互式桌面
CloseDesktop(hDesk);
}
逻辑分析:
OpenInputDesktop获取当前活动用户桌面句柄;SetThreadDesktop将服务主线程上下文切换至该桌面,使后续CreateWindowEx创建的窗口可见且可接收输入。注意:此操作需服务账户具有SE_TCB_NAME权限(即“作为操作系统的一部分运行”)。
推荐架构对比
| 方案 | 适用场景 | 安全性 | Windows 10+ 兼容性 |
|---|---|---|---|
| Interactive Service | 本地调试/遗留系统 | ⚠️ 较低(需提升权限) | ❌ 已禁用 |
| Session 0 隔离 + 命名管道代理 | 生产环境 | ✅ 高 | ✅ 原生支持 |
| WinRT AppService + Background Task | UWP 集成 | ✅ 高 | ✅ 推荐新项目 |
graph TD
A[ServiceStart] --> B{是否需要GUI?}
B -->|是| C[切换到用户会话桌面]
B -->|否| D[纯后台模式]
C --> E[创建隐藏主窗口]
E --> F[注入WebView2或DUI线程]
3.2 使用sc.exe与PowerShell双路径完成服务注册与参数配置
Windows服务部署需兼顾兼容性与可编程性,sc.exe适用于批处理与旧环境,PowerShell则提供结构化、可验证的现代管理能力。
基于sc.exe注册带启动参数的服务
sc create "MyAppSvc" binPath= "C:\app\service.exe --config C:\app\svc.conf --log-level warn" start= auto obj= "NT AUTHORITY\LocalService"
binPath=后必须含完整可执行路径及空格分隔的参数;start=支持auto/demand/disabled;obj=指定运行账户,影响权限边界与资源访问能力。
PowerShell方式(推荐用于CI/CD)
New-Service -Name "MyAppSvc" -BinaryPathName '"C:\app\service.exe" --config "C:\app\svc.conf" --log-level warn' -StartupType Automatic -Credential "NT AUTHORITY\LocalService"
引号嵌套需谨慎:外层双引号包裹整个命令行,内部路径用双引号转义,避免参数截断。
| 工具 | 优势 | 局限 |
|---|---|---|
sc.exe |
系统内置、无依赖 | 参数解析脆弱、无返回对象 |
| PowerShell | 支持错误捕获、管道集成 | 需PowerShell 5.1+ |
graph TD
A[服务注册需求] --> B[sc.exe:快速脚本化]
A --> C[PowerShell:可审计、可测试]
B --> D[适用于遗留部署]
C --> E[适配Azure DevOps/YAML流水线]
3.3 服务权限模型:LocalSystem、NetworkService与自定义账户适配
Windows 服务运行账户的选择直接影响安全边界与资源访问能力。三类主流账户模型特性对比如下:
| 账户类型 | 权限级别 | 网络身份 | 典型适用场景 |
|---|---|---|---|
LocalSystem |
系统级最高 | 计算机名$(域内) | 需驱动交互或本地特权操作 |
NetworkService |
低权限受限 | 计算机名$(匿名网络) | 仅需基础网络通信的服务 |
| 自定义域账户 | 可精确控制 | 显式指定域用户 | 需访问特定共享/数据库场景 |
权限配置示例(SC命令)
# 将服务 MySvc 运行账户设为 NetworkService
sc config MySvc obj= "NT AUTHORITY\NetworkService"
# 启用服务时自动加载用户配置文件(需额外权限)
sc config MySvc type= own type= interact
obj= 指定安全主体;type= interact 允许与桌面会话交互(仅限 LocalSystem 或交互式服务)。
权限演进路径
- 初始开发:LocalSystem(快速验证功能)
- 测试阶段:NetworkService(降低攻击面)
- 生产部署:最小权限域账户(遵循 Principle of Least Privilege)
graph TD
A[LocalSystem] -->|高风险| B[NetworkService]
B -->|更细粒度控制| C[自定义域账户]
C --> D[基于组策略的动态权限委派]
第四章:服务启动、调试与异常恢复机制
4.1 服务启动流程剖析:从StartService到OnStart事件触发链
Windows 服务启动并非简单调用,而是一条跨进程、跨权限边界的内核级调用链。
核心触发路径
- SCM(服务控制管理器)接收
StartService()API 请求 - 验证服务账户权限与二进制路径有效性
- 通过
CreateProcessAsUser()以指定上下文启动服务宿主进程(如svchost.exe) - 宿主进程加载服务 DLL,调用其
ServiceMain()入口 - 最终派发至托管服务的
OnStart(string[] args)方法
// ServiceBase 派生类中重写的 OnStart
protected override void OnStart(string[] args)
{
// args 来自 SCM 启动命令行参数(非 Windows 服务配置中的“启动参数”)
// 例如:sc start MyService arg1 arg2 → args = ["arg1", "arg2"]
EventLog.WriteEntry("Service started with " + args.Length + " arguments");
}
该方法在服务进程主线程同步执行;若超时(默认30秒),SCM 将标记启动失败。
关键状态流转
| 阶段 | SCM 状态 | 内部标志 |
|---|---|---|
| StartService 调用后 | SERVICE_START_PENDING | bStarting = true |
| ServiceMain 返回 | SERVICE_RUNNING | bStarted = true |
| OnStart 抛出异常 | SERVICE_STOPPED | bFailed = true |
graph TD
A[StartService API] --> B[SCM 验证并创建进程]
B --> C[LoadLibrary + ServiceMain]
C --> D[ServiceBase.Run → WndProc 消息循环]
D --> E[WM_START → OnStart]
4.2 开发期调试技巧:模拟服务上下文与AttachConsole方案
在本地开发阶段,服务常依赖运行时上下文(如 IHostEnvironment、ILogger<T>、配置绑定),直接启动 Host 易受环境干扰。此时需轻量级上下文模拟。
模拟服务上下文
var host = Host.CreateEmptyBuilder()
.ConfigureServices(services =>
{
services.AddSingleton<ILogger<Program>>(sp =>
new ConsoleLogger<Program>()); // 替代注入的 ILoggerFactory
services.Configure<AppSettings>(new ConfigurationBuilder()
.AddInMemoryCollection(new[] { new KeyValuePair<string, string>("Api:Timeout", "3000") })
.Build());
})
.Build();
该方式绕过完整 Host 生命周期,仅构建 DI 容器与基础服务,适用于单元测试或快速验证逻辑;AddInMemoryCollection 提供可编程配置源,避免读取真实 appsettings.json。
AttachConsole 方案
| 场景 | 优势 | 注意事项 |
|---|---|---|
| 后台服务/Worker | 实时输出日志到控制台 | 需手动调用 host.Services.GetRequiredService<ILogger<Program>>() |
| 无 UI 的 CLI 工具 | 避免重写日志管道 | 不自动启用 ConsoleLoggerProvider,需显式注册 |
graph TD
A[启动空 Host] --> B[注册模拟服务]
B --> C[AttachConsole:重定向 ILogger 输出]
C --> D[执行业务逻辑]
4.3 自动重启策略:FailureActions与ResetPeriod配置实践
Windows 服务可通过 sc 命令或服务控制管理器(SCM)配置故障后自动响应行为,核心在于 FailureActions 与 ResetPeriod 的协同。
FailureActions 结构解析
该值为二进制数据,需通过 sc failure 命令设置。典型配置:
sc failure "MyService" reset= 86400 actions= restart/60000/restart/60000/run/300000
reset= 86400:重置失败计数器周期(秒),即 24 小时;actions=后每三项为「动作/延迟/动作」三元组:restart/60000表示首次失败后 60 秒重启,run/300000表示第三次失败后 5 分钟运行自定义恢复脚本。
ResetPeriod 作用域
| 参数名 | 类型 | 说明 |
|---|---|---|
| ResetPeriod | DWORD | 失败计数清零时间(秒),默认 0(永不重置) |
| RebootMsg | STRING | 重启前显示消息(可选) |
策略生效流程
graph TD
A[服务崩溃] --> B{失败次数 < 阈值?}
B -->|是| C[执行对应action]
B -->|否| D[触发最后动作:reboot/run]
C --> E[等待Delay后执行]
E --> F[重置计时器?→ 取决ResetPeriod]
合理设置可避免雪崩式重启,保障服务韧性。
4.4 服务崩溃捕获:Windows事件日志写入与ExitCode语义化处理
当Windows服务非正常终止时,仅依赖GetLastError()无法还原上下文。需结合结构化日志与语义化退出码实现可观测性。
日志写入核心逻辑
使用ReportEventW将崩溃上下文写入系统事件日志:
// 示例:记录服务崩溃事件(需管理员权限)
BOOL WriteCrashEvent(DWORD exitCode, LPCWSTR crashContext) {
HANDLE hEventLog = RegisterEventSourceW(NULL, L"MyService");
if (!hEventLog) return FALSE;
LPCWSTR strings[] = { crashContext, L"ExitCode=0x", NULL };
strings[2] = (LPCWSTR)((DWORD_PTR)(exitCode)); // 实际需格式化为宽字符串
BOOL bRet = ReportEventW(hEventLog,
EVENTLOG_ERROR_TYPE,
0, 0x1001, NULL, 3, 0, strings, NULL);
DeregisterEventSource(hEventLog);
return bRet;
}
ReportEventW参数说明:eventID=0x1001表示预定义崩溃事件;strings数组提供上下文变量;EVENTLOG_ERROR_TYPE确保被归类为错误级别。
ExitCode语义化映射表
| ExitCode | 含义 | 建议动作 |
|---|---|---|
0xC0000005 |
访问违规(AV) | 检查指针解引用 |
0xE06D7363 |
C++异常(MSVC ABI) | 分析SEH/VEH链 |
0x80000003 |
断点中断 | 排查调试器残留 |
崩溃捕获流程
graph TD
A[服务进程异常终止] --> B{是否注册VEH?}
B -->|是| C[VEH捕获EXCEPTION_ACCESS_VIOLATION等]
B -->|否| D[由系统默认处理并生成minidump]
C --> E[调用WriteCrashEvent写入事件日志]
E --> F[返回语义化ExitCode给SCM]
第五章:golang注册为windows服务
Go 语言编写的后台程序在 Windows 生产环境中常需以系统服务方式长期稳定运行。直接双击启动或使用 cmd 手动运行存在进程易被误关、系统重启后不自启、无权限访问系统资源等问题。本章基于真实部署场景,详解如何将 Go 程序注册为 Windows 服务,并确保其具备日志持久化、异常自动恢复与标准服务生命周期控制能力。
依赖工具选型与对比
| 工具 | 是否需管理员权限 | 是否支持服务安装/卸载命令行 | 是否内置日志重定向 | 是否兼容 Windows Server 2012+ |
|---|---|---|---|---|
github.com/kardianos/service |
是 | ✅(install/uninstall) |
✅(自动写入 Windows 事件日志) | ✅ |
github.com/sevlyar/go-daemon |
否 | ❌(需自行封装 sc.exe 调用) | ❌(需手动实现) | ⚠️(部分版本存在 Session 0 隔离问题) |
推荐采用 kardianos/service —— 它是社区事实标准,已被 Consul、Nomad 等主流项目验证,且提供跨平台抽象层。
服务结构代码示例
package main
import (
"log"
"time"
"github.com/kardianos/service"
)
type program struct{}
func (p *program) Start(s service.Service) error {
go p.run()
return nil
}
func (p *program) Stop(s service.Service) error {
log.Println("Service stopping...")
return nil
}
func (p *program) run() {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for range ticker.C {
log.Printf("Heartbeat at %s", time.Now().Format(time.RFC3339))
}
}
func main() {
svcConfig := &service.Config{
Name: "GoAppMonitor",
DisplayName: "Go Application Monitor Service",
Description: "Monitors system metrics and reports to central dashboard",
}
prg := &program{}
s, err := service.New(prg, svcConfig)
if err != nil {
log.Fatal(err)
}
if len(service.Args()) != 0 {
service.Control(s, service.Args()[0])
return
}
err = s.Run()
if err != nil {
log.Fatal(err)
}
}
安装与调试流程
- 以管理员身份打开 PowerShell;
- 编译为 Windows 可执行文件:
GOOS=windows GOARCH=amd64 go build -o monitor-service.exe .; - 执行
.\monitor-service.exe install注册服务; - 使用
sc query GoAppMonitor验证状态; - 若启动失败,通过
Get-EventLog -LogName Application -Source "GoAppMonitor" -Newest 20查看详细错误事件; - 日志默认写入 Windows 事件查看器 → 应用程序日志 → 来源为服务名。
权限配置要点
Windows 服务默认运行于 LocalSystem 账户,但若需访问网络共享或用户配置文件,应在服务属性中切换至专用域账户,并勾选“登录为服务”权限(通过 secpol.msc → 本地策略 → 用户权限分配配置)。忽略此步将导致 Access is denied 错误且事件日志仅显示模糊的 Error 1053。
升级服务二进制文件的安全操作
不可直接覆盖正在运行的服务 .exe 文件。正确流程为:
- 停止服务:
net stop GoAppMonitor; - 替换二进制:
copy /Y new-monitor-service.exe monitor-service.exe; - 重启服务:
net start GoAppMonitor; - 验证事件日志中出现新版本启动标记(如
Starting v1.4.2)。
服务注册表项位于 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\GoAppMonitor,其中 ImagePath 值必须为绝对路径,且建议避免含空格路径(否则需额外转义引号)。
flowchart TD
A[管理员启动PowerShell] --> B[执行 install 命令]
B --> C{服务注册成功?}
C -->|是| D[sc query 确认状态为 RUNNING]
C -->|否| E[检查事件查看器 Application 日志]
D --> F[观察每5秒 Heartbeat 日志]
E --> G[定位 ERROR 1067 或 1053 根因]
G --> H[修正权限/路径/依赖缺失] 