👀 长时间盯着屏幕眼睛酸痛?用几行 PowerShell 脚本就能打造一个轻量的 Windows 托盘护眼助手,
支持定时提醒、系统托盘运行、自定义图标、自动锁屏、以及开机自启。
✨ 功能简介
本教程实现一个轻量的 护眼提醒工具 EyeRest:
🕒 每隔 N 分钟提醒你休息眼睛(自定义时间)
💬 桌面右下角弹出提示气泡
🔒 提示后自动锁屏(可选)
🧠 托盘图标运行,不占用任务栏
⚙️ 右键托盘菜单可:
修改提醒时间
手动退出
打开/关闭「开机自启」
🖼️ 可自定义托盘图标(ICO)
🚀 打包成单个
.exe,方便分享给电脑小白使用
🧩 一、准备工作
1. 所需文件
一个
.ps1脚本(主程序)一个
.ico图标文件(自定义托盘图标)
文件结构如下:
EyeRest/
├─ EyeRest.ps1 # 主脚本(你将要编写)
└─ eye.ico # 托盘图标
2. 安装 ps2exe 打包工具
打开 PowerShell(以管理员身份)运行:
Install-Module -Name ps2exe -Force
💡 二、脚本核心逻辑
脚本使用以下几个 .NET 命名空间:
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
System.Windows.Forms.NotifyIcon:实现托盘图标System.Timers.Timer:实现定时提醒System.Windows.Forms.ContextMenuStrip:右键菜单rundll32.exe user32.dll,LockWorkStation:锁屏命令
📜 三、完整源码
💾 将以下内容保存为
EyeRest.ps1
👉 源码开始:
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# ICO Base64 字符串
$iconBase64 = @"
<BASE64_STRING>
"@ # 省略中间内容,完整 Base64 写入
# 从 Base64 创建图标对象
$iconBytes = [System.Convert]::FromBase64String($iconBase64)
$ms = New-Object System.IO.MemoryStream(,$iconBytes)
$icon = New-Object System.Drawing.Icon($ms)
# 托盘图标
$notifyIcon = New-Object System.Windows.Forms.NotifyIcon
$notifyIcon.Icon = $icon
$notifyIcon.Text = "护眼提醒-IWA"
$notifyIcon.Visible = $true
# ============================
# 默认提醒间隔(分钟)
$intervalMinutes = 30
# 是否启用自动锁屏
$global:autoLockEnabled = $true
# 创建右键菜单
$menu = New-Object System.Windows.Forms.ContextMenuStrip
# 设置提醒间隔菜单
$setIntervalItem = $menu.Items.Add("设置提醒间隔")
$setIntervalItem.Add_Click({
# 创建自定义输入框
$form = New-Object System.Windows.Forms.Form
$form.Text = "设置提醒间隔(分钟)"
$form.Size = New-Object System.Drawing.Size(300,150)
$form.StartPosition = "CenterScreen"
$form.FormBorderStyle = "FixedDialog"
$form.MaximizeBox = $false
$form.MinimizeBox = $false
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Size = New-Object System.Drawing.Size(260,20)
$textBox.Location = New-Object System.Drawing.Point(10,20)
$textBox.Text = $intervalMinutes
$form.Controls.Add($textBox)
$okButton = New-Object System.Windows.Forms.Button
$okButton.Text = "确定"
$okButton.Location = New-Object System.Drawing.Point(60,60)
$okButton.Add_Click({ $form.Tag = $textBox.Text; $form.Close() })
$form.Controls.Add($okButton)
$cancelButton = New-Object System.Windows.Forms.Button
$cancelButton.Text = "取消"
$cancelButton.Location = New-Object System.Drawing.Point(150,60)
$cancelButton.Add_Click({ $form.Tag = $null; $form.Close() })
$form.Controls.Add($cancelButton)
$form.Topmost = $true
$form.ShowDialog() | Out-Null
if ($form.Tag -ne $null) {
if ([int]::TryParse($form.Tag,[ref]$null) -and [int]$form.Tag -gt 0) {
$global:intervalMinutes = [int]$form.Tag
$timer.Interval = $intervalMinutes * 60 * 1000
[System.Windows.Forms.MessageBox]::Show("提醒间隔已设置为 $intervalMinutes 分钟","提示",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Information)
} else {
[System.Windows.Forms.MessageBox]::Show("请输入有效的整数分钟数。","错误",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Error)
}
}
})
# 自动锁屏开关菜单
$autoLockItem = $menu.Items.Add("自动锁屏:开")
$autoLockItem.Add_Click({
if ($global:autoLockEnabled) {
$global:autoLockEnabled = $false
$autoLockItem.Text = "自动锁屏:关"
[System.Windows.Forms.MessageBox]::Show("已关闭自动锁屏功能","提示",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Information)
} else {
$global:autoLockEnabled = $true
$autoLockItem.Text = "自动锁屏:开"
[System.Windows.Forms.MessageBox]::Show("已开启自动锁屏功能","提示",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Information)
}
})
# ==========================
# 开机自启菜单逻辑(修正版)
# ==========================
$regKey = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run"
$regValueName = "EyeRest"
# 获取当前程序路径(支持 exe / ps1)
try {
$exePath = [System.Diagnostics.Process]::GetCurrentProcess().MainModule.FileName
} catch {
if ($MyInvocation.MyCommand.Path) {
$exePath = $MyInvocation.MyCommand.Path
} else {
$exePath = (Join-Path $PSScriptRoot "EyeRest.exe")
}
}
function Get-AutoStartStatus {
try {
$value = (Get-ItemProperty -Path $regKey -Name $regValueName -ErrorAction SilentlyContinue).$regValueName
return ($value -eq $exePath)
} catch {
return $false
}
}
function Set-AutoStart($enable) {
if ($enable) {
Set-ItemProperty -Path $regKey -Name $regValueName -Value $exePath
} else {
Remove-ItemProperty -Path $regKey -Name $regValueName -ErrorAction SilentlyContinue
}
}
# 初始化菜单项文字
$isAutoStart = Get-AutoStartStatus
$autoStartText = if ($isAutoStart) { "开机自启:开" } else { "开机自启:关" }
$autoStartItem = $menu.Items.Add($autoStartText)
# 点击事件
$autoStartItem.Add_Click({
$current = Get-AutoStartStatus
if ($current) {
Set-AutoStart $false
$autoStartItem.Text = "开机自启:关"
[System.Windows.Forms.MessageBox]::Show("已关闭开机自启","提示",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Information)
} else {
Set-AutoStart $true
$autoStartItem.Text = "开机自启:开"
[System.Windows.Forms.MessageBox]::Show("已开启开机自启","提示",[System.Windows.Forms.MessageBoxButtons]::OK,[System.Windows.Forms.MessageBoxIcon]::Information)
}
})
# 退出菜单
$exitItem = $menu.Items.Add("退出")
$exitItem.Add_Click({
$notifyIcon.Visible = $false
[System.Windows.Forms.Application]::Exit()
})
$notifyIcon.ContextMenuStrip = $menu
# 提醒函数
function Show-Reminder {
$notifyIcon.BalloonTipTitle = "IWA的艺术编程,休息一下 👀"
$notifyIcon.BalloonTipText = "$intervalMinutes 分钟到了,离开屏幕看看远处!"
if ($global:autoLockEnabled) {
$notifyIcon.BalloonTipText += "`n10 秒后将自动锁屏。"
}
$notifyIcon.BalloonTipIcon = [System.Windows.Forms.ToolTipIcon]::Info
$notifyIcon.ShowBalloonTip(5000)
# 延迟 10 秒后锁屏
if ($global:autoLockEnabled) {
Start-Job -ScriptBlock {
Start-Sleep -Seconds 10
rundll32.exe user32.dll,LockWorkStation
} | Out-Null
}
}
# 定时器
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = $intervalMinutes * 60 * 1000
$timer.Add_Tick({ Show-Reminder })
$timer.Start()
# 启动时先提醒一次
Show-Reminder
# 保持托盘应用运行
[System.Windows.Forms.Application]::Run()
# $iconBytes = [System.IO.File]::ReadAllBytes("Desktop/iwa.ico")
# [System.IO.File]::WriteAllText("eye.txt", [System.Convert]::ToBase64String($iconBytes))
# ps2exe -inputFile "C:\Users\my\Desktop\EyeRest-IWA.ps1" -outputFile "C:\Users\my\Desktop\护眼提醒-IWA.exe" -noConsole -iconFile "C:\Users\my\Desktop\iwa.ico"
👈 源码结束
🧰 四、打包成 EXE
在 PowerShell 中运行以下命令:
ps2exe -inputFile "C:\Users\my\Desktop\EyeRest\EyeRest.ps1" `
-outputFile "C:\Users\my\Desktop\EyeRest\EyeRest.exe" `
-noConsole `
-iconFile "C:\Users\my\Desktop\EyeRest\eye.ico"
参数说明:
🔧 五、运行效果
双击运行
EyeRest.exe系统托盘出现你的自定义图标
每隔 N 分钟弹出休息提醒
提醒后 10 秒自动锁屏
右键图标菜单可修改时间或退出
🔄 六、开机自启说明
启用“开机自启”后,脚本会在注册表路径:
HKCU\Software\Microsoft\Windows\CurrentVersion\Run
自动创建一条记录:
名称:EyeRest
数据:"C:\Users\my\Desktop\EyeRest\EyeRest.exe"
再次点击即可关闭自启。
⚠️ 七、常见问题
🎁 八、效果图
🧠 九、总结
这个小工具的关键点在于:
PowerShell + .NET 能轻松调用系统组件;
ps2exe 可将脚本封装成单个可执行文件;
利用注册表管理自启;
全程无第三方依赖,轻量可靠。
📦 十、下载与分享
我已经将打包好的
EyeRest.exe上传到附件中,你也可以直接下载!
🧑💻 作者:Rock
📅 更新日期:2025-10-23
💬 如果这篇教程对你有帮助,欢迎点赞 / 收藏 / 分享
🧩 解决托盘图标不随 EXE 打包的问题:使用 Base64 内嵌图标
因为 ps2exe 在打包时不会自动把外部 .ico 文件一起打包进可执行文件中,所以当别人运行时,图标路径就会失效。
最好的做法是:把图标文件转成 Base64 字符串,写入脚本内部,运行时自动解码生成临时图标。
第一步:把 .ico 转成 Base64 字符串
在 PowerShell 中执行以下命令:
$iconBytes = [System.IO.File]::ReadAllBytes("C:\Users\my\Desktop\eye.ico")
[System.IO.File]::WriteAllText("C:\Users\my\Desktop\eye_base64.txt", [System.Convert]::ToBase64String($iconBytes))
执行后会生成一个文件:
C:\Users\my\Desktop\eye_base64.txt
里面是一长串 Base64 编码文本。
第二步:在脚本中加载这个 Base64 图标
在你的主脚本中(例如 EyeRest.ps1),添加以下代码片段到顶部区域(初始化托盘图标之前):
# ==== 图标加载(Base64 内嵌) ====
# 把下面的 <BASE64_STRING> 替换成你生成的 eye_base64.txt 内容
$base64Icon = @"
<BASE64_STRING>
"@
# 运行时将 Base64 转换为临时图标文件
$tempIconPath = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "eye.ico")
[System.IO.File]::WriteAllBytes($tempIconPath, [System.Convert]::FromBase64String($base64Icon))
$icon = [System.Drawing.Icon]::ExtractAssociatedIcon($tempIconPath)
✅ 注意:打包前记得替换
<BASE64_STRING>为你自己的内容。
第三步:重新打包
然后重新打包:
ps2exe -inputFile "C:\Users\my\Desktop\EyeRest.ps1" -outputFile "C:\Users\my\Desktop\EyeRest.exe" -noConsole
现在生成的 EXE 不再依赖外部图标文件


默认评论
Halo系统提供的评论