# 写了个简单的 Windows 服务程序

因为一些个人需求，想给家里的台式机加个开机通知的工具。一开始的解决方案是开机后自动执行 Python 脚本，然后发送邮件来通知，而现在看到了可以用 C# 写 Windows 服务程序来实现，于是折腾的劲就上来了。在这里记录下这两天写的小工具。

在写 Windows 服务时也是打算用邮件的方式去通知，但当时写着写着就想，也就通知个信息而已，至于用一个邮箱去给另一个邮箱发送邮件吗？这也太奇怪了，于是我就想像苹果那样可以通过系统推送服务给手机发送通知。

网上的 Apple 推送接口教程对我来说最难的一步是开发者账号和生成证书，后面我也大致看了下小米和华为的推送服务，最后我选用了 [WxPusher (微信推送服务)](https://wxpusher.zjiecode.com)，不用 [Server 酱](https://sct.ftqq.com)是因为只能发送五条信息，不够用。

### 1. Push 方法调用后没收到通知

这是我第一个遇到的问题，至于为啥我会遇到这个问题纯属是我经验不够。

在断点调试后，我发现问题在 Json 字符串数据的拼接这块代码，我是直接将接收到的参数拼接在字符串里，此时拼接后是字符串了，但不是 Json 字符串，因为它的值没有被引号包括，导致没有成功发送请求。

![1](https://cn-img.owoser.cn/images/2023/08/22/e98a6036eb7e30f880b480e9f1f5b1e4.png)

为啥不用 Json 对象通过 Add 方法动态增加内容，再转为字符串？因为没必要多加一个库去处理，本来也就这点点文本。

### 2. 编译好的程序安装不了

根据逻辑写好代码，配置好安装程序后，根本安装不了服务，错误信息说的是 `出现System.Security.SecurityException: 未找到源，但未能搜索某些或全部事件日志。不可访问的日志: Security。` 网上的答案几乎都是说权限不够，但我确确实实是以管理员身份运行的 Bat，怎么可能没有权限啊。

后面我根据一些检索到的信息，发现是安装程序属性的 `ServiceName` 和服务程序 cs 文件属性的 `ServiceName` 不一致，而文档说的是要一致。

### 3. 在 OnStop 和 OnShutdown 事件里调用 Push 方法后没收到通知

测试时候，只有在 OnStart 事件里调用 Push 方法有发送通知外，其余两个事件根本没有完成 Push 方法里面的请求。通过断点调试查找问题时，发现每次发起请求后就没啥反应了，表现为执行到 `await client.PostAsync` 后就结束了，啥也没发生，通知也没收到。

对于这个问题我也不知道咋解决，于是我就用 ChatGPT 帮忙排查解决了，ChatGPT 说到：

```
使用异步方法需要注意：Push 方法被声明为异步方法，但在 OnStop 中直接调用异步方法可能会导致问题，因为在服务停止时可能无法等待异步操作完成。这可能会导致异步操作未能完成或引发异常。在 OnStop 中调用异步方法时，最好使用 .Wait() 或 .GetAwaiter().GetResult() 等方法来同步等待异步操作的完成。
```

我根据它的提示和代码，把 Push 方法 void 类型改为 async Task，在调用时加个 Wait() 方法就行了，修改后终于是成功收到通知了。

### 4. 家里的台式机在开机和关机时没有发送通知

在虚拟机测试时是可以用的，结果一放到台式机后就跟“失灵”了一样，没反应。

在台式机的服务管理器那手动启动和关闭服务都是正常发送通知的，但就是开机和关机过程没有发送通知。而且开机后，服务管理器里显示这个服务是有运行的，也就是说它没有调用到 Push 方法，这就奇怪了啊，虚拟机里是正常的。我还发现只有重启电脑时，才有收到通知。

后面检索到一篇文章说关闭快速启动就可以了，果然，已关闭这个设置后就正常了。