panrouter 1.2.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.mjs +15 -16
- package/daemon.mjs +71 -102
- package/package.json +2 -2
- package/{tray-manager.ps1 → tray-daemon.ps1} +33 -45
package/cli.mjs
CHANGED
|
@@ -138,19 +138,21 @@ async function startServer() {
|
|
|
138
138
|
console.log("\n 现在可以运行: \x1b[33mclaude \"你好\"\x1b[0m\n");
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
// ─── 4.
|
|
141
|
+
// ─── 4. 以托盘模式启动 ──────────────────────────────────────────────────
|
|
142
142
|
|
|
143
143
|
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
144
|
+
* 启动策略 (参考 9Router Windows 托盘):
|
|
145
|
+
*
|
|
146
|
+
* cli.mjs ─spawn(detached)──→ node daemon.mjs (无窗口, 常驻)
|
|
147
|
+
* ├─ cmd /c start /B → node server.mjs (隐藏)
|
|
148
|
+
* └─ spawn(pipe) ───→ powershell tray-daemon.ps1
|
|
149
|
+
* └─ NotifyIcon ✓
|
|
150
|
+
*
|
|
151
|
+
* 关键: PS 必须用 pipe 连接(不 detached), 保持与 daemon 的 Window Station
|
|
152
|
+
* 连接, 否则无法创建通知区图标。
|
|
150
153
|
*/
|
|
151
154
|
function startTray() {
|
|
152
155
|
const daemonPath = path.join(__dirname, "daemon.mjs");
|
|
153
|
-
|
|
154
156
|
if (!fs.existsSync(daemonPath)) {
|
|
155
157
|
log("!!", "未找到 daemon.mjs", "red");
|
|
156
158
|
process.exit(1);
|
|
@@ -158,23 +160,20 @@ function startTray() {
|
|
|
158
160
|
|
|
159
161
|
log("..", "正在以托盘模式启动 Pan Router...", "yellow");
|
|
160
162
|
|
|
161
|
-
const serverPath = path.join(__dirname, "server.mjs");
|
|
162
|
-
const trayPath = path.join(__dirname, "tray-manager.ps1");
|
|
163
|
-
|
|
164
163
|
const child = spawn(process.execPath, [
|
|
165
164
|
daemonPath,
|
|
166
|
-
`--serverPath="${
|
|
167
|
-
`--
|
|
165
|
+
`--serverPath="${path.join(__dirname, "server.mjs")}"`,
|
|
166
|
+
`--trayPsPath="${path.join(__dirname, "tray-daemon.ps1")}"`,
|
|
168
167
|
], {
|
|
169
|
-
cwd: __dirname,
|
|
170
168
|
stdio: "ignore",
|
|
171
|
-
detached: true,
|
|
172
169
|
windowsHide: true,
|
|
170
|
+
detached: true,
|
|
173
171
|
shell: false,
|
|
174
172
|
});
|
|
175
173
|
child.unref();
|
|
176
174
|
|
|
177
|
-
log("OK", "Pan Router
|
|
175
|
+
log("OK", "Pan Router 托盘已启动", "green");
|
|
176
|
+
console.log(" 图标在任务栏右下角, 右键菜单可退出");
|
|
178
177
|
}
|
|
179
178
|
|
|
180
179
|
// ─── 主流程 ──────────────────────────────────────────────────────────────
|
package/daemon.mjs
CHANGED
|
@@ -3,11 +3,17 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Pan Router Daemon — 后台守护进程
|
|
5
5
|
*
|
|
6
|
-
* 由 cli.mjs
|
|
7
|
-
*
|
|
6
|
+
* 用法 (由 cli.mjs --tray 调用):
|
|
7
|
+
* node daemon.mjs --serverPath="..." --trayPsPath="..."
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* 架构 (参考 9Router):
|
|
10
|
+
* daemon.mjs (无窗口, 常驻)
|
|
11
|
+
* ├─ spawn server.mjs (hidden, UseShellExecute=true)
|
|
12
|
+
* └─ spawn powershell tray-daemon.ps1 (pipe, NOT detached)
|
|
13
|
+
* └─ NotifyIcon (右下角)
|
|
14
|
+
*
|
|
15
|
+
* 关键: PS 子进程不 detached, 保持与 daemon 的 session 连接,
|
|
16
|
+
* 否则失去 Window Station 无法创建 UI 通知区图标。
|
|
11
17
|
*/
|
|
12
18
|
|
|
13
19
|
import { spawn, execSync } from "node:child_process";
|
|
@@ -21,22 +27,19 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
21
27
|
|
|
22
28
|
// ─── 解析参数 ────────────────────────────────────
|
|
23
29
|
const serverPath = process.argv.find(a => a.startsWith("--serverPath="))?.split("=")[1];
|
|
24
|
-
const
|
|
25
|
-
if (!serverPath || !
|
|
30
|
+
const trayPsPath = process.argv.find(a => a.startsWith("--trayPsPath="))?.split("=")[1];
|
|
31
|
+
if (!serverPath || !trayPsPath) { process.exit(1); }
|
|
26
32
|
|
|
27
33
|
const appDir = path.dirname(serverPath);
|
|
28
|
-
const AUTOSTART_NAME = "PanRouter";
|
|
29
|
-
const RUN_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
|
|
30
|
-
|
|
31
|
-
let serverProcess = null;
|
|
32
|
-
let psProcess = null;
|
|
33
|
-
|
|
34
|
-
// ─── 日志 ────────────────────────────────────────
|
|
35
34
|
const LOG = path.join(process.env.TEMP || "/tmp", "panrouter-daemon.log");
|
|
35
|
+
|
|
36
36
|
function log(msg) {
|
|
37
37
|
try { fs.appendFileSync(LOG, `${new Date().toISOString().slice(11,19)} ${msg}\n`); } catch {}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
log("=== PanRouter Daemon (v2) ===");
|
|
41
|
+
log(`serverPath=${serverPath}`);
|
|
42
|
+
|
|
40
43
|
// ─── 健康检查 ─────────────────────────────────────
|
|
41
44
|
function isOnline() {
|
|
42
45
|
return new Promise(rs => {
|
|
@@ -47,8 +50,9 @@ function isOnline() {
|
|
|
47
50
|
});
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
// ───
|
|
51
|
-
|
|
53
|
+
// ─── 启动隐藏的 server.mjs ────────────────────────
|
|
54
|
+
const serverProcess = (() => {
|
|
55
|
+
// 先杀旧的
|
|
52
56
|
try {
|
|
53
57
|
const wmic = spawn("wmic", [
|
|
54
58
|
"process", "where", "name='node.exe'", "get", "ProcessId,CommandLine", "/format:csv"
|
|
@@ -64,66 +68,20 @@ function killOldServers() {
|
|
|
64
68
|
}
|
|
65
69
|
});
|
|
66
70
|
} catch {}
|
|
67
|
-
}
|
|
68
71
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
log("Starting server...");
|
|
73
|
+
const child = spawn("cmd.exe", ["/c", "start", "/B", "node", serverPath], {
|
|
74
|
+
cwd: appDir,
|
|
75
|
+
stdio: "ignore",
|
|
76
|
+
windowsHide: true,
|
|
77
|
+
shell: false,
|
|
74
78
|
});
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// ─── 命令 → PS 托盘 ─────────────────────────────
|
|
82
|
-
function psSend(cmd) {
|
|
83
|
-
if (psProcess?.stdin?.writable) {
|
|
84
|
-
psProcess.stdin.write(JSON.stringify(cmd) + "\n");
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// ─── 开机自启动操作 (reg.exe) ────────────────────
|
|
89
|
-
function toggleAutostart() {
|
|
90
|
-
try {
|
|
91
|
-
const out = execSync(`reg query ${RUN_KEY} /v ${AUTOSTART_NAME} 2>nul`, {
|
|
92
|
-
encoding: "utf8", windowsHide: true, timeout: 3000,
|
|
93
|
-
});
|
|
94
|
-
const isOn = !out.toLowerCase().includes("error");
|
|
95
|
-
if (isOn) {
|
|
96
|
-
execSync(`reg delete ${RUN_KEY} /v ${AUTOSTART_NAME} /f 2>nul`, { windowsHide: true, timeout: 3000 });
|
|
97
|
-
psSend({ action: "update-item", index: 2, title: "开机自启动", enabled: true });
|
|
98
|
-
log("Autostart OFF");
|
|
99
|
-
} else {
|
|
100
|
-
const exe = process.execPath;
|
|
101
|
-
const daemon = path.join(__dirname, "daemon.mjs");
|
|
102
|
-
const cmd = `"${exe}" "${daemon}" --serverPath="${serverPath}" --trayPath="${trayPath}"`;
|
|
103
|
-
execSync(`reg add ${RUN_KEY} /v ${AUTOSTART_NAME} /t REG_SZ /d "${cmd}" /f 2>nul`, { windowsHide: true, timeout: 3000 });
|
|
104
|
-
psSend({ action: "update-item", index: 2, title: "✓ 开机自启动", enabled: true });
|
|
105
|
-
log("Autostart ON");
|
|
106
|
-
}
|
|
107
|
-
} catch (e) { log(`Autostart error: ${e.message}`); }
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// ─── 退出清理 ────────────────────────────────────
|
|
111
|
-
function cleanup() {
|
|
112
|
-
log("Cleanup...");
|
|
113
|
-
try { if (serverProcess && !serverProcess.killed) serverProcess.kill("SIGKILL"); } catch {}
|
|
114
|
-
killOldServers();
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// ═══════════════ 主流程 ═══════════════
|
|
118
|
-
|
|
119
|
-
log("=== PanRouter Daemon ===");
|
|
120
|
-
log(`serverPath=${serverPath}`);
|
|
121
|
-
log(`trayPath=${trayPath}`);
|
|
122
|
-
|
|
123
|
-
// 1. 启动服务器
|
|
124
|
-
startServer();
|
|
79
|
+
child.unref();
|
|
80
|
+
log(`Server started via cmd /c start /B`);
|
|
81
|
+
return child;
|
|
82
|
+
})();
|
|
125
83
|
|
|
126
|
-
//
|
|
84
|
+
// ─── 等服务器就绪 ─────────────────────────────────
|
|
127
85
|
(async () => {
|
|
128
86
|
let ready = false;
|
|
129
87
|
for (let i = 0; i < 20; i++) {
|
|
@@ -132,47 +90,58 @@ startServer();
|
|
|
132
90
|
}
|
|
133
91
|
log(`Server ready=${ready}`);
|
|
134
92
|
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
|
|
93
|
+
// ─── 启动 PS 托盘 (pipe, 不 detached!) ──────────
|
|
94
|
+
// 关键: 9Router 的做法 - PS 必须与 daemon 同 session
|
|
95
|
+
log("Starting PS tray...");
|
|
96
|
+
const psProcess = spawn("powershell.exe", [
|
|
97
|
+
"-NoProfile",
|
|
98
|
+
"-ExecutionPolicy", "Bypass",
|
|
138
99
|
"-WindowStyle", "Hidden",
|
|
139
|
-
"-File",
|
|
100
|
+
"-File", trayPsPath,
|
|
140
101
|
], {
|
|
141
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
102
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
103
|
+
windowsHide: true,
|
|
104
|
+
shell: false,
|
|
105
|
+
// ⚠ 没有 detached: true — PS 进程保持子进程身份
|
|
142
106
|
});
|
|
143
107
|
|
|
144
|
-
// 读取 PS 事件
|
|
108
|
+
// IPC 读取 PS 事件
|
|
145
109
|
createInterface({ input: psProcess.stdout }).on("line", (line) => {
|
|
146
110
|
try {
|
|
147
111
|
const evt = JSON.parse(line);
|
|
148
|
-
log(`PS: ${
|
|
149
|
-
if (evt.type === "
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
psSend({ action: "set-tooltip", text: "Pan Router | 端口 50816" });
|
|
157
|
-
log("Menu configured");
|
|
158
|
-
}
|
|
159
|
-
if (evt.type === "click" && evt.index === 2) toggleAutostart();
|
|
160
|
-
if (evt.type === "click" && evt.index === 4) {
|
|
161
|
-
log("Exit requested");
|
|
162
|
-
cleanup();
|
|
163
|
-
psSend({ action: "kill" });
|
|
164
|
-
setTimeout(() => process.exit(0), 500);
|
|
112
|
+
log(`PS event: ${evt.type} idx=${evt.index}`);
|
|
113
|
+
if (evt.type === "click") {
|
|
114
|
+
if (evt.index === 2) { // 退出
|
|
115
|
+
log("Exit from PS menu");
|
|
116
|
+
try { if (serverProcess && !serverProcess.killed) serverProcess.kill(); } catch {}
|
|
117
|
+
psSend({ action: "kill" });
|
|
118
|
+
setTimeout(() => process.exit(0), 500);
|
|
119
|
+
}
|
|
165
120
|
}
|
|
166
|
-
} catch (e) { log(`PS parse
|
|
121
|
+
} catch (e) { log(`PS parse err: ${e.message}`); }
|
|
167
122
|
});
|
|
168
123
|
|
|
169
124
|
psProcess.on("error", err => log(`PS error: ${err.message}`));
|
|
170
|
-
psProcess.stderr.on("data", d => log(`[ps
|
|
171
|
-
psProcess.on("exit", code => {
|
|
125
|
+
psProcess.stderr.on("data", d => log(`[ps] ${d.toString().trim()}`));
|
|
126
|
+
psProcess.on("exit", code => {
|
|
127
|
+
log(`PS exited code=${code}`);
|
|
128
|
+
process.exit(0);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// ─── 发送菜单配置到 PS ──────────────────────────
|
|
132
|
+
function psSend(cmd) {
|
|
133
|
+
if (psProcess?.stdin?.writable) {
|
|
134
|
+
psProcess.stdin.write(JSON.stringify(cmd) + "\n");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
psSend({ action: "add-item", index: 0, title: "Pan Router - :50816", enabled: false });
|
|
138
|
+
psSend({ action: "add-item", index: 1, title: "─".repeat(19), enabled: false });
|
|
139
|
+
psSend({ action: "add-item", index: 2, title: "退出", enabled: true });
|
|
140
|
+
psSend({ action: "set-tooltip", text: "Pan Router | 端口 50816" });
|
|
172
141
|
|
|
173
|
-
log("Daemon
|
|
174
|
-
process.stdin.resume(); //
|
|
142
|
+
log("Daemon running — keeping session alive");
|
|
143
|
+
process.stdin.resume(); // keep alive
|
|
175
144
|
})();
|
|
176
145
|
|
|
177
|
-
process.on("SIGTERM", () => {
|
|
178
|
-
process.on("SIGINT",
|
|
146
|
+
process.on("SIGTERM", () => { log("SIGTERM"); process.exit(0); });
|
|
147
|
+
process.on("SIGINT", () => { log("SIGINT"); process.exit(0); });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "panrouter",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "让 Claude Code 免费使用 DeepSeek 等模型,无需 API Key",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"cli.mjs",
|
|
11
11
|
"daemon.mjs",
|
|
12
12
|
"server.mjs",
|
|
13
|
-
"tray-
|
|
13
|
+
"tray-daemon.ps1"
|
|
14
14
|
],
|
|
15
15
|
"license": "MIT"
|
|
16
16
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
<#
|
|
2
2
|
.SYNOPSIS
|
|
3
|
-
Pan Router
|
|
4
|
-
.DESCRIPTION
|
|
5
|
-
纯 NotifyIcon 包装器, 通过 stdin/stdout JSON 与父进程通信
|
|
3
|
+
Pan Router 托盘进程 — 纯 NotifyIcon IPC 包装器
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
IPC: stdin JSON 命令, stdout JSON 事件
|
|
6
|
+
|
|
7
|
+
命令 (stdin):
|
|
8
8
|
{"action":"add-item","index":0,"title":"...","enabled":true}
|
|
9
9
|
{"action":"update-item","index":0,"title":"...","enabled":true}
|
|
10
10
|
{"action":"set-tooltip","text":"..."}
|
|
@@ -12,34 +12,17 @@
|
|
|
12
12
|
|
|
13
13
|
事件 (stdout):
|
|
14
14
|
{"type":"started"}
|
|
15
|
-
{"type":"ready"} <- PS 就绪 + STA 已确认
|
|
16
15
|
{"type":"click","index":0}
|
|
17
16
|
{"type":"error","message":"..."}
|
|
18
17
|
#>
|
|
19
18
|
|
|
20
|
-
param()
|
|
21
|
-
|
|
22
19
|
Add-Type -AssemblyName System.Windows.Forms
|
|
23
20
|
Add-Type -AssemblyName System.Drawing
|
|
24
21
|
|
|
25
22
|
$ErrorActionPreference = "Stop"
|
|
26
23
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
27
24
|
|
|
28
|
-
|
|
29
|
-
$script:notifyIcon.Visible = $true
|
|
30
|
-
$script:menu = New-Object System.Windows.Forms.ContextMenuStrip
|
|
31
|
-
$script:notifyIcon.ContextMenuStrip = $script:menu
|
|
32
|
-
$script:items = @()
|
|
33
|
-
|
|
34
|
-
function Write-Event($obj) {
|
|
35
|
-
$json = $obj | ConvertTo-Json -Compress
|
|
36
|
-
try {
|
|
37
|
-
[Console]::Out.WriteLine($json)
|
|
38
|
-
[Console]::Out.Flush()
|
|
39
|
-
} catch {}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
# ─── 生成蓝色 P 图标 (纯内存, 无需 .ico 文件) ──
|
|
25
|
+
# ─── 生成图标 (蓝色 P, 纯内存) ────────────────────
|
|
43
26
|
$bmp = New-Object System.Drawing.Bitmap(16, 16)
|
|
44
27
|
$g = [System.Drawing.Graphics]::FromImage($bmp)
|
|
45
28
|
$g.SmoothingMode = 'HighQuality'
|
|
@@ -54,8 +37,22 @@ $hIcon = $bmp.GetHicon()
|
|
|
54
37
|
$icon = [System.Drawing.Icon]::FromHandle($hIcon)
|
|
55
38
|
$bmp.Dispose()
|
|
56
39
|
|
|
57
|
-
|
|
58
|
-
$
|
|
40
|
+
# ─── NotifyIcon ──────────────────────────────────
|
|
41
|
+
$notifyIcon = New-Object System.Windows.Forms.NotifyIcon
|
|
42
|
+
$notifyIcon.Icon = $icon
|
|
43
|
+
$notifyIcon.Text = "Pan Router | 端口 50816"
|
|
44
|
+
$notifyIcon.Visible = $true
|
|
45
|
+
|
|
46
|
+
$menu = New-Object System.Windows.Forms.ContextMenuStrip
|
|
47
|
+
$notifyIcon.ContextMenuStrip = $menu
|
|
48
|
+
$items = @()
|
|
49
|
+
|
|
50
|
+
function Write-Event($obj) {
|
|
51
|
+
try {
|
|
52
|
+
[Console]::Out.WriteLine(($obj | ConvertTo-Json -Compress))
|
|
53
|
+
[Console]::Out.Flush()
|
|
54
|
+
} catch {}
|
|
55
|
+
}
|
|
59
56
|
|
|
60
57
|
function Add-MenuItem($index, $title, $enabled) {
|
|
61
58
|
$item = New-Object System.Windows.Forms.ToolStripMenuItem
|
|
@@ -63,26 +60,19 @@ function Add-MenuItem($index, $title, $enabled) {
|
|
|
63
60
|
$item.Enabled = $enabled
|
|
64
61
|
$idx = $index
|
|
65
62
|
$item.Add_Click({ Write-Event @{type="click"; index=$idx} }.GetNewClosure())
|
|
66
|
-
$
|
|
67
|
-
$
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function Update-MenuItem($index, $title, $enabled) {
|
|
71
|
-
if ($index -lt $script:items.Count) {
|
|
72
|
-
$script:items[$index].Text = $title
|
|
73
|
-
$script:items[$index].Enabled = $enabled
|
|
74
|
-
}
|
|
63
|
+
$menu.Items.Add($item) | Out-Null
|
|
64
|
+
$items += $item
|
|
75
65
|
}
|
|
76
66
|
|
|
77
67
|
function Set-Tooltip($text) {
|
|
78
68
|
if ($text.Length -gt 63) { $text = $text.Substring(0, 63) }
|
|
79
|
-
$
|
|
69
|
+
$notifyIcon.Text = $text
|
|
80
70
|
}
|
|
81
71
|
|
|
82
|
-
# ─── stdin 轮询 (100ms
|
|
83
|
-
$
|
|
84
|
-
$
|
|
85
|
-
$
|
|
72
|
+
# ─── stdin 轮询 (9Router 做法: 100ms Timer) ──────
|
|
73
|
+
$timer = New-Object System.Windows.Forms.Timer
|
|
74
|
+
$timer.Interval = 100
|
|
75
|
+
$timer.Add_Tick({
|
|
86
76
|
try {
|
|
87
77
|
while ([Console]::In.Peek() -ne -1) {
|
|
88
78
|
$line = [Console]::In.ReadLine()
|
|
@@ -90,22 +80,20 @@ $script:timer.Add_Tick({
|
|
|
90
80
|
$cmd = $line | ConvertFrom-Json
|
|
91
81
|
switch ($cmd.action) {
|
|
92
82
|
"add-item" { Add-MenuItem $cmd.index $cmd.title $cmd.enabled }
|
|
93
|
-
"update-item" {
|
|
83
|
+
"update-item" { }
|
|
94
84
|
"set-tooltip" { Set-Tooltip $cmd.text }
|
|
95
85
|
"kill" {
|
|
96
|
-
$
|
|
97
|
-
$
|
|
86
|
+
$notifyIcon.Visible = $false
|
|
87
|
+
$notifyIcon.Dispose()
|
|
98
88
|
$icon.Dispose()
|
|
99
89
|
[System.Runtime.InteropServices.Marshal]::DestroyIcon($hIcon)
|
|
100
90
|
[System.Windows.Forms.Application]::Exit()
|
|
101
91
|
}
|
|
102
92
|
}
|
|
103
93
|
}
|
|
104
|
-
} catch {
|
|
105
|
-
Write-Event @{type="error"; message=$_.Exception.Message}
|
|
106
|
-
}
|
|
94
|
+
} catch { Write-Event @{type="error"; message=$_.Exception.Message} }
|
|
107
95
|
})
|
|
108
|
-
$
|
|
96
|
+
$timer.Start()
|
|
109
97
|
|
|
110
98
|
Write-Event @{type="started"}
|
|
111
99
|
[System.Windows.Forms.Application]::Run()
|