简介WinSW 是一款开源轻量级的 Windows 服务包装工具主要面向开发人员与系统管理员用于把 .NET、Java 或自定义可执行程序注册为 Windows 系统服务从而获得开机自启、后台常驻与统一服务管理的能力。本资源包共 3 个文件包含 64 位与 32 位两个可执行程序及一份最小化 XML 配置示例分别对应不同架构的 Windows 系统与配置模板压缩包约 11.2MB。其中 exe 为服务包装主程序xml 用于声明服务名称、启动命令、参数与日志方式等关键项配置完成后即可通过标准服务管理工具控制启动、停止与恢复。目前已有 544 人学习下载适合需要将 Java 应用、.NET 程序或批处理脚本长期稳定运行于 Windows 后台的读者参考可快速理解服务包装思路并落地到实际部署场景。1. 从三个文件说起WinSW 把 exe 当服务跑到底解决了谁的痛手里拿到WinSW-x64.exe、WinSW-x86.exe、sample-minimal.xml这三个文件的人多半正卡在同一个场景里有个程序只能双击运行或者只能用命令行start拉起来一旦关掉终端窗口它就跟着没了服务器重启之后更是彻底失联。你想让它像 Windows 服务一样开机自启、后台常驻、崩溃能拉起来但又不想为了这点事去写一整套 C 服务框架。WinSW 就是干这个的——它是一个通用的服务包装器把任意可执行文件、脚本、Java 程序包成标准的 Windows 服务交给 SCM服务控制管理器托管。x64 和 x86 两个 exe 是同一套逻辑的两个架构版本sample-minimal.xml则是最小配置模板告诉你配置文件长什么样。这篇笔记就围绕这三个文件把选型、配置、安装、排错和进阶玩法一次讲透适合需要把自研程序或第三方工具做成常驻服务的运维和开发。2. 选对 exe 与配置文件WinSW 的架构判断和最小配置长什么样2.1 x64 还是 x86先看宿主系统再看目标程序很多人拿到两个 exe 的第一反应是「随便挑一个能跑就行」结果在 64 位系统上用了 x86 版本服务能起来但包装的目标程序如果是 64 位的就会出现启动即退出、日志里只有一行莫名其妙的错误。判断逻辑其实很简单WinSW 自身作为宿主进程它的位数决定了它能否加载目标程序的位数。64 位 Windows 上如果目标程序是 64 位就必须用WinSW-x64.exe如果目标程序是 32 位两个版本理论上都能拉起它但推荐还是用 x64 宿主减少一层兼容层开销。只有在极老的 32 位 Windows Server 上才被迫用WinSW-x86.exe。判断宿主系统位数不用翻系统信息一条命令就够# 在 cmd 或 PowerShell 中执行返回 x64 或 x86 echo %PROCESSOR_ARCHITECTURE%返回AMD64或ARM64说明是 64 位系统用 x64 版本返回x86才是真正的 32 位系统。注意32 位程序在 64 位系统上运行时这个变量可能被 WOW64 重定向显示为 x86所以更稳妥的方式是看PROCESSOR_ARCHITEW6432是否存在存在就说明底层是 64 位。选定 exe 之后把它重命名成一个有意义的名字比如myservice.exe这是 WinSW 的约定服务名和 exe 名、配置文件名的前缀保持一致。也就是说如果你把 exe 改名为myservice.exe那么配置文件就必须叫myservice.xml安装命令里用的也是myservice.exe install。这个约定不遵守服务能装上但读不到配置启动直接失败。2.2 sample-minimal.xml 里真正必须改的只有几行sample-minimal.xml是官方给的最小骨架长这样service idsample/id nameSample Service/name descriptionThis is a sample service./description executablejava/executable arguments-jar %BASE%\sample.jar/arguments logmoderotate/logmode /service逐行拆开看id是服务在 SCM 里的唯一标识安装后sc query看到的就是它不能和已有服务重名name是服务管理器里显示的名字可以带空格和中文description是描述鼠标悬停时显示executable是要拉起的程序可以是绝对路径也可以是 PATH 里的命令arguments是传给它的参数%BASE%是 WinSW 内置变量指向配置文件所在目录这个变量在写相对路径时非常关键logmode控制日志策略rotate表示按大小轮转append表示追加none表示不记日志。最小配置能跑但生产环境至少要补三样东西workingdirectory指定工作目录否则目标程序取相对路径会以C:\Windows\System32为基准这是血泪经验onfailure actionrestart delay10 sec/让服务崩溃后自动重启stoptimeout15 sec/stoptimeout给优雅停止留出时间。补完之后的配置更接近可用状态service idmyservice/id nameMy Service/name descriptionMy wrapped service./description executable%BASE%\app\server.exe/executable arguments--config %BASE%\app\conf.ini/arguments workingdirectory%BASE%\app/workingdirectory logmoderotate/logmode onfailure actionrestart delay10 sec/ stoptimeout15 sec/stoptimeout /service这里%BASE%出现了三次全部指向配置文件所在目录这样整个服务目录可以整体搬移不用改配置。参数里的路径用双引号包住防止路径中有空格时被拆成多个参数这是 Windows 命令行解析的经典坑。2.3 安装、启动、卸载的标准动作配置写好后用管理员权限打开 cmd进入 exe 所在目录依次执行# 安装服务读取同名 xml 配置 myservice.exe install # 启动服务 myservice.exe start # 查看状态 myservice.exe status # 停止并卸载 myservice.exe stop myservice.exe uninstallinstall这一步会把服务注册到 SCM并设置启动类型为自动。如果只想手动启动加--startup manual。卸载前必须先停止否则会提示服务正在运行。如果安装时报「服务已存在」说明之前装过没清干净用sc delete 服务名强制删除后再装。提示所有 install、uninstall、start、stop 命令都必须在管理员权限的终端里执行普通权限会报拒绝访问这个错误信息往往很含糊容易误判成配置问题。3. 日志、重启与停止把服务行为调成你想要的样子3.1 日志配置决定了排错时你有没有后悔药WinSW 默认会在 exe 同目录生成服务名.out.log、服务名.err.log、服务名.wrapper.log三个文件。.out.log是目标程序的标准输出.err.log是标准错误.wrapper.log是 WinSW 自身的日志服务起不来时第一个要看的就是它。logmode的取值直接决定这些文件怎么增长logmode 取值行为适用场景append一直追加不轮转调试期文件小rotate按大小轮转默认 10MB 一个生产环境推荐roll-by-time按时间轮转日志量稳定的场景none不写日志目标程序自己管日志用rotate时还可以配logpath指定日志目录避免日志和程序混在一起。如果目标程序自己会写日志把logmode设成none能省掉一层重复记录但.wrapper.log仍然会生成因为那是 WinSW 自己的诊断通道关不掉也不该关。3.2 onfailure 的重启策略要配合 stoptimeout 一起调onfailure支持三种动作restart、reboot、none。生产环境基本只用restart但重启不是无脑配的。默认情况下服务异常退出后 WinSW 会立即重启如果目标程序本身有启动即崩的问题就会陷入「崩溃—重启—再崩溃」的死循环日志瞬间被刷爆。合理的做法是加延迟和次数限制onfailure actionrestart delay10 sec/ onfailure actionrestart delay30 sec/ resetfailure1 hour/resetfailure这里写了两条onfailure表示第一次失败等 10 秒重启第二次失败等 30 秒重启resetfailure表示服务稳定运行 1 小时后重置失败计数。这样既能扛住偶发崩溃又不会在持续故障时疯狂重启。stoptimeout控制的是停止服务时等待目标程序退出的最长时间。默认值偏短如果目标程序需要时间刷盘或释放连接会被强杀导致数据损坏。设成 15 到 30 秒比较稳妥具体看程序自身的关闭逻辑。如果程序响应 SIGTERM 类的停止信号WinSW 会先尝试优雅停止超时后才强杀。3.3 用 stoparguments 和 stopparentprocessfirst 处理顽固进程有些程序不认标准的停止信号或者会 fork 出子进程导致主进程停了子进程还在。这时候需要两个配置项配合stopparentprocessfirsttrue/stopparentprocessfirst stoparguments-stop/stopargumentsstopparentprocessfirst为 true 时WinSW 先停父进程再停子进程适合那种父进程只是启动器、真正干活的是子进程的场景。stoparguments则是给目标程序传一个专门的停止参数比如很多 Java 服务支持-stop来触发优雅关闭。这两个配置不是万能的如果目标程序完全不响应任何停止机制最终还是会走到强杀这时候要考虑在程序层面加信号处理。注意stopparentprocessfirst和stoparguments同时使用时停止参数是传给父进程的子进程的停止依赖父进程自己处理配置前先确认程序的进程模型。4. 避坑与排查WinSW 装服务时最容易翻车的五个地方4.1 服务装上了但启动即停止日志里只有一行 exit code现象myservice.exe start返回成功但status立刻显示 stopped.wrapper.log里只有exit code 1之类的信息看不到具体原因。原因目标程序启动失败但它的错误输出没有被正确捕获或者工作目录不对导致它找不到依赖文件。解决先把executable和arguments拼成一条完整命令在 cmd 里手动执行一遍确认程序本身能跑起来。然后检查workingdirectory是否设成了程序所在目录。如果程序依赖环境变量在配置里用env nameXXX valueYYY/显式传入不要指望它继承系统的。4.2 中文路径或空格路径导致参数被截断现象程序在英文无空格路径下正常换到C:\Program Files\我的服务\就启动失败。原因Windows 命令行解析把空格当参数分隔符中文路径在某些编码下还会乱码。解决所有路径用双引号包住executable和arguments里的路径都要包。配置文件本身保存为 UTF-8 无 BOM 格式避免中文乱码。如果还是不行把程序挪到无空格无中文的路径下这是最省事的做法。4.3 卸载后重新安装报服务已存在现象uninstall执行了但再install时提示服务已存在。原因uninstall只是标记删除SCM 里可能还有残留或者有同名的其他服务。解决用sc query 服务名确认是否真的还在在的话用sc delete 服务名强制删除。删除后等几秒再安装SCM 有缓存。如果服务名和 id 不一致两个都要检查。4.4 服务启动超时被 SCM 判定为失败现象程序启动本身需要 30 秒以上SCM 在默认超时时间内没收到「已启动」信号直接标记失败并触发 onfailure。原因WinSW 向 SCM 报告启动成功的时机和目标程序实际就绪时间不一致。解决WinSW 本身没有直接的「启动等待」配置常见做法是在目标程序层面加快启动或者用prestart和poststop钩子做前置检查。如果程序启动就是慢考虑把服务启动类型设为手动由外部脚本在程序就绪后再调start。4.5 日志文件把磁盘写满现象服务跑了一段时间后磁盘告警发现.out.log涨到了几十 GB。原因logmode设成了append或者目标程序自己疯狂输出而 WinSW 的 rotate 阈值设得太大。解决生产环境统一用rotate并显式设置logpath到一个独立分区。如果目标程序输出量极大在程序层面降低日志级别WinSW 的轮转只是兜底不是治本。5. 进阶玩法用 prestart/poststop 钩子和环境变量把服务管起来5.1 用 prestart 做启动前检查避免带病启动WinSW 支持prestart和poststop两个钩子分别在服务启动前和停止后执行。典型用法是启动前检查依赖服务是否就绪prestart executablecmd/executable arguments/c ping -n 1 dbhost nul 21 || exit 1/arguments /prestart这段配置在启动前 ping 一下数据库主机不通就返回非零退出码WinSW 会中止启动并记录到日志。这样能避免程序在依赖不可用时反复崩溃重启。poststop可以用来做清理比如删除临时文件或发送通知。钩子的执行是同步的prestart 不返回服务就不会启动所以钩子里的命令要快不要放耗时操作。钩子的输出同样会进.wrapper.log排错时能看到。5.2 环境变量和 JVM 参数的注入方式如果目标程序是 Java 应用executable写javaarguments里放-jar和 JVM 参数。内存参数、编码参数都在这里配executablejava/executable arguments-Xms512m -Xmx1024m -Dfile.encodingUTF-8 -jar %BASE%\app\service.jar/arguments env nameJAVA_HOME valueC:\Java\jdk-17/env可以配多个每个环境变量一行。注意JAVA_HOME这类变量如果系统里已经有了这里配的会覆盖它所以值要写对。JVM 参数里的-Dfile.encodingUTF-8在 Windows 上尤其重要不配的话中文日志大概率乱码。5.3 验证服务是否真的在按预期工作装完服务不是看status显示 running 就完事了。一套完整的验证动作包括重启服务器确认服务自动起来手动 kill 目标进程确认 onfailure 在延迟后把它拉起来往日志目录写满测试文件确认 rotate 生效用sc qc 服务名看启动类型和依赖关系是否符合预期。这几步走完才算真正把这个服务管住了。我自己现在的习惯是每配一个新服务先在测试机上把上面这套验证跑一遍尤其是重启验证很多配置问题只有在重启后才会暴露。配置文件的备份也留一份改坏了能快速回滚。希望帮到你。本文还有配套的精品资源点击获取