【原创工具】简单实现云控Win电脑锁屏
手机云控 Windows 电脑锁屏
背景
在工作时,常会遇到这么一个场景:坐在电脑前办公,突然被叫了出去,可能出去只有一两分钟或几分钟,因此我不会将电脑锁屏,同时由于常用电脑做一些前台任务,电脑也不设置自动锁屏,但中断事件的时间是无法确定的,如果几十分钟甚至一两个小时还不回来,不锁屏的电脑无异于一台任人抚摸的小猫——吸引人且无力防御。
设计思路
反复几次后,我就萌生了写一个能远程云控电脑锁屏的小工具的想法,那样我只要在手机上设置一下锁屏,办公室的电脑就能自动锁屏了。思路也很简单:
A[用户操作] -->|云端控制台设置锁屏状态| B[发送锁屏状态至服务器]
B --> C{服务器}
C -->|存储状态| D[被控电脑]
D -->|定期查询服务器状态| E{状态判断}
E -->|“锁屏”| F[执行锁屏操作]
E -->|“未锁屏”| G[保持当前状态]
F --> D
G --> D
实现方案
按照上述思路,实现起来就很简单了。可以拆分成以下几个小步骤:
- 写一个云控控制台,能在手机上访问
- 云端控制器能存储当前状态,并供受控端读取
- 写一个锁屏器,在受控电脑上安装,自动获取云端当前状态,并实现锁屏操作
具体实现如下:
- 云端控制台为了让通用性更强,且更简化、更快捷实现,选择了网页端来部署,没有选择用 APP,不然还要考虑 Android/IOS/Mac 等各大系统适配。
- 状态存储器用 php 实现,原因:代码简单易写,服务器方便部署且通用性强。
- 锁屏器用 golang 来写,最初考虑过 Python,但综合考量打包体积、跨设备兼容和交叉编译支持情况,还是选择了 golang。
实现代码
云端控制台,一个 html 静态小网页
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>远程锁屏控制面板 - 怜渠客</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#3B82F6',
secondary: '#10B981',
danger: '#EF4444',
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
},
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.content-auto {
content-visibility: auto;
}
.btn {
@apply px-4 py-2 rounded-lg font-medium transition-all duration-300 transform hover:scale-105 focus:outline-none focus:ring-2 focus:ring-opacity-50;
}
.btn-primary {
@apply bg-primary text-white focus:ring-primary;
}
.btn-danger {
@apply bg-danger text-white focus:ring-danger;
}
.status-indicator {
@apply w-4 h-4 rounded-full inline-block mx-2 transition-all duration-500;
}
.status-indicator-locked {
@apply bg-danger;
}
.status-indicator-unlocked {
@apply bg-secondary;
}
}
</style>
</head>
<body class="bg-gray-100 min-h-screen flex flex-col">
<div class="container mx-auto px-4 py-8 max-w-3xl">
<header class="text-center mb-8">
<h1 class="text-[clamp(1.8rem,4vw,2.5rem)] font-bold text-gray-800">远程锁屏控制面板</h1>
<p class="text-gray-600 mt-2">通过此面板管理和查看远程设备的锁屏状态</p>
</header>
<div class="bg-white rounded-xl shadow-lg p-6 mb-8 transform transition-all duration-500 hover:shadow-xl">
<div class="flex items-center justify-between mb-6">
<div class="flex items-center">
<span id="status-text" class="text-lg font-medium">当前状态: 未锁定</span>
<span id="status-indicator" class="status-indicator status-indicator-unlocked"></span>
</div>
<span id="last-updated" class="text-sm text-gray-500">上次更新: 刚刚</span>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<button id="lock-btn" class="btn btn-danger flex items-center justify-center">
<i class="fa fa-lock mr-2"></i> 设置为锁屏状态
</button>
<button id="unlock-btn" class="btn btn-primary flex items-center justify-center">
<i class="fa fa-unlock mr-2"></i> 设置为不锁屏状态
</button>
</div>
<div id="message" class="mt-4 p-3 rounded-lg hidden transition-all duration-500"></div>
</div>
<div class="bg-gray-800 text-white rounded-xl p-4">
<h2 class="text-xl font-semibold mb-2 flex items-center">
<i class="fa fa-info-circle mr-2"></i> 使用说明
</h2>
<ul class="list-disc pl-5 space-y-1 text-gray-300">
<li>点击"设置为锁屏状态"按钮,远程设备将在下次检查时锁定屏幕</li>
<li>点击"设置为不锁屏状态"按钮,远程设备将保持解锁状态</li>
</ul>
</div>
</div>
<footer class="mt-auto py-4 bg-gray-800 text-white text-center">
<p>远程锁屏系统 2025 By 怜渠客</p>
</footer>
<script>
// API端点
const API_URL = '###目标服务器地址###/state_query.php';
// DOM元素
const lockBtn = document.getElementById('lock-btn');
const unlockBtn = document.getElementById('unlock-btn');
const statusText = document.getElementById('status-text');
const statusIndicator = document.getElementById('status-indicator');
const lastUpdated = document.getElementById('last-updated');
const message = document.getElementById('message');
// 更新状态显示
function updateStatusDisplay(state) {
if (state === '0') {
statusText.textContent = '当前状态: 已锁定';
statusIndicator.className = 'status-indicator status-indicator-locked';
} else {
statusText.textContent = '当前状态: 未锁定';
statusIndicator.className = 'status-indicator status-indicator-unlocked';
}
const now = new Date();
const timeString = now.toLocaleTimeString();
lastUpdated.textContent = `上次更新: ${timeString}`;
}
// 显示消息
function showMessage(text, isError = false) {
message.textContent = text;
message.className = `mt-4 p-3 rounded-lg transition-all duration-500 ${
isError ? 'bg-red-100 text-red-700' : 'bg-green-100 text-green-700'
}`;
message.classList.remove('hidden');
// 3秒后隐藏消息
setTimeout(() => {
message.classList.add('hidden');
}, 3000);
}
// 获取当前状态
async function fetchStatus() {
try {
const response = await fetch(`${API_URL}?ask=1`);
if (!response.ok) {
throw new Error(`HTTP错误,状态码: ${response.status}`);
}
const state = await response.text();
updateStatusDisplay(state.trim());
return state.trim();
} catch (error) {
console.error('获取状态失败:', error);
showMessage('获取状态失败,请稍后再试', true);
return null;
}
}
// 设置状态
async function setStatus(newState) {
try {
const response = await fetch(`${API_URL}?set=${newState}`);
if (!response.ok) {
throw new Error(`HTTP错误,状态码: ${response.status}`);
}
const result = await response.text();
showMessage(`状态已成功设置为: ${newState === '0' ? '锁定' : '未锁定'}`);
// 立即刷新状态
fetchStatus();
} catch (error) {
console.error('设置状态失败:', error);
showMessage('设置状态失败,请稍后再试', true);
}
}
// 事件监听器
lockBtn.addEventListener('click', () => setStatus('0'));
unlockBtn.addEventListener('click', () => setStatus('1'));
// 初始化页面
document.addEventListener('DOMContentLoaded', () => {
// 立即获取一次状态
fetchStatus();
});
</script>
</body>
</html>
云端 php 代码
<?php
// 状态文件路径
$stateFile = __DIR__ . '/state.txt';
// 初始化状态文件(如果不存在)
if (!file_exists($stateFile)) {
file_put_contents($stateFile, '1');
}
// 处理GET请求
if (isset($_GET['ask'])) {
// 查询状态
$state = file_get_contents($stateFile);
echo trim($state);
} elseif (isset($_GET['set'])) {
// 设置状态
$newState = $_GET['set'];
if ($newState === '0' || $newState === '1') {
file_put_contents($stateFile, $newState);
echo trim($newState);
} else {
http_response_code(400);
echo "error1";
}
} else {
// 无效请求
http_response_code(400);
echo "error2";
}
?>
只是为了向同目录下的 state.txt 里写入或读取当前状态,0 或 1
golang 锁屏器
package main
import (
"fmt"
"github.com/getlantern/systray"
"github.com/lxn/win"
"io/ioutil"
"log"
"net/http"
"syscall"
"time"
)
const (
checkInterval = 5 // 检查间隔(秒)
stateURL = "###目标服务器地址###/state_query.php"
)
func main() {
systray.Run(onReady, onExit)
}
func onReady() {
systray.SetTitle("状态监控")
systray.SetTooltip("系统状态监控程序")
// 创建菜单项
mOpen := systray.AddMenuItem("打开", "打开主窗口")
systray.AddSeparator()
mQuit := systray.AddMenuItem("退出", "退出程序")
// 初始隐藏控制台窗口
hideConsole()
// 启动状态检查协程
go func() {
log.Println("开始监控状态...")
ticker := time.NewTicker(time.Second * checkInterval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
checkState()
case <-mQuit.ClickedCh:
systray.Quit()
return
case <-mOpen.ClickedCh:
showConsole()
}
}
}()
}
func onExit() {
log.Println("程序已退出")
}
// 检查状态并根据结果锁屏
func checkState() {
resp, err := http.Get(fmt.Sprintf("%s?ask=1", stateURL))
if err != nil {
log.Printf("请求异常: %v\n", err)
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Printf("请求失败,状态码: %d\n", resp.StatusCode)
return
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("读取响应失败: %v\n", err)
return
}
content := string(body)
if len(content) == 0 {
log.Println("收到空响应")
setTrayIcon("unknown")
return
}
firstChar := content[0]
switch firstChar {
case '0':
log.Println("状态为0,执行锁屏")
lockScreen()
setTrayIcon("locked")
case '1':
log.Println("状态为1,不执行操作")
setTrayIcon("unlocked")
default:
log.Printf("收到未知响应首字符: %c (完整响应: %s)\n", firstChar, content)
setTrayIcon("unknown")
}
}
// 执行Windows锁屏操作
func lockScreen() {
dll, err := syscall.LoadDLL("user32.dll")
if err != nil {
log.Printf("加载user32.dll失败: %v\n", err)
return
}
defer dll.Release()
proc, err := dll.FindProc("LockWorkStation")
if err != nil {
log.Printf("查找LockWorkStation函数失败: %v\n", err)
return
}
r1, _, err := proc.Call()
if r1 == 0 {
log.Printf("锁屏失败: %v\n", err)
} else {
log.Println("已锁屏")
}
}
// 设置托盘图标状态
func setTrayIcon(status string) {
// 这里简化处理,实际应用中可以使用不同的图标资源
switch status {
case "locked":
systray.SetTooltip("系统状态监控程序 - 已锁定")
case "unlocked":
systray.SetTooltip("系统状态监控程序 - 未锁定")
case "unknown":
systray.SetTooltip("系统状态监控程序 - 未知状态")
}
}
// 隐藏控制台窗口
func hideConsole() {
hwnd := win.GetConsoleWindow()
if hwnd != 0 {
var processID uint32
win.GetWindowThreadProcessId(hwnd, &processID)
if GetCurrentProcessId() == processID {
win.ShowWindow(hwnd, win.SW_HIDE)
}
}
}
// 获取当前进程ID
func GetCurrentProcessId() uint32 {
kernel32, err := syscall.LoadDLL("kernel32.dll")
if err != nil {
return 0
}
proc, err := kernel32.FindProc("GetCurrentProcessId")
if err != nil {
return 0
}
r1, _, _ := proc.Call()
return uint32(r1)
}
// 显示控制台窗口
func showConsole() {
hwnd := win.GetConsoleWindow()
if hwnd != 0 {
win.ShowWindow(hwnd, win.SW_SHOW)
}
}
cmd 将可执行程序添加到自启动
@echo off
setlocal
:: 设置应用程序名称和路径(可修改为实际程序路径)
set "APP_NAME=状态监控程序"
set "APP_PATH=%~dp0monitor.exe" :: 当前目录下的monitor.exe,可修改名称,可替换为完整路径
:: 注册表路径
set "REG_PATH=HKCU\Software\Microsoft\Windows\CurrentVersion\Run"
:menu
cls
echo =====================================
echo 怜渠客云控自启动管理工具
echo =====================================
echo.
echo 1. 添加程序到自启动
echo 2. 从自启动中移除程序
echo 0. 退出
echo.
set /p choice=请选择操作 [0-2]:
if "%choice%"=="1" goto add
if "%choice%"=="2" goto remove
if "%choice%"=="0" goto end
goto menu
:add
:: 检查路径是否存在
if not exist "%APP_PATH%" (
echo 错误:程序路径不存在 - %APP_PATH%
echo 请修改脚本中的APP_PATH变量为正确路径
goto :pause
)
:: 尝试添加注册表自启动项
echo 正在添加自启动项...
reg add "%REG_PATH%" /v "%APP_NAME%" /t REG_SZ /d "%APP_PATH%" /f
:: 检查操作结果
if %errorLevel% equ 0 (
echo 自启动项添加成功!
echo 程序将在下次启动时自动运行
) else (
echo 自启动项添加失败!
echo 请尝试以管理员身份运行此脚本
)
goto :pause
:remove
:: 尝试删除注册表自启动项
echo 正在移除自启动项...
reg delete "%REG_PATH%" /v "%APP_NAME%" /f
:: 检查操作结果
if %errorLevel% equ 0 (
echo 自启动项已成功移除!
) else (
echo 自启动项移除失败!
echo 可能该自启动项不存在,或需要管理员权限
)
goto :pause
:pause
echo.
pause
goto menu
:end
echo 程序已退出。
endlocal
另存为.bat 文件后,以管理员权限运行,这个是为了添加编译好的 golang 程序添加到开机自启动,就能自动后台运行了。
额外补充(可以不用)
同时也用 Python 实现了一个锁屏器,虽然最后没有采用。
import requests
import time
import subprocess
import os
CHECK_INTERVAL = 5 # 检查间隔(秒)
STATE_URL = "###目标服务器地址###/state_query.php"
def lock_screen():
"""执行Windows锁屏操作"""
try:
subprocess.run("rundll32.exe user32.dll,LockWorkStation", check=True)
print("已锁屏")
except subprocess.CalledProcessError as e:
print(f"锁屏失败: {e}")
except Exception as e:
print(f"发生未知错误: {e}")
def main():
"""主程序:循环检查状态并根据结果锁屏"""
print(f"开始监控状态,每{CHECK_INTERVAL}秒检查一次...")
while True:
try:
response = requests.get(f"{STATE_URL}?ask=1", timeout=10)
if response.status_code == 200:
content = response.text.strip()
if content == "0":
print("状态为0,执行锁屏")
lock_screen()
elif content == "1":
print("状态为1,不执行操作")
else:
print(f"收到未知响应: {content}")
else:
print(f"请求失败,状态码: {response.status_code}")
except requests.exceptions.RequestException as e:
print(f"请求异常: {e}")
except Exception as e:
print(f"发生未知错误: {e}")
time.sleep(CHECK_INTERVAL)
if __name__ == "__main__":
main()
部署
代码是如何实现的,无关紧要,只要能实现功能即可。那如何部署使用呢?
首先,控制台网页要部署到服务器(所以需要一台服务器,唯一的额外成本,小型轻量云即可,大约 50 元/年)
注意:html 里的服务器地址要改成自己的

然后,php 代码要传到服务器,才能在控制台和锁屏器里访问。

第三,将前面 go 代码里的服务器地址改成自己的,编译成 exe,并使用.bat添加到开机自启动
这里需要一些 golang 开发经验,至少知道怎么打包。

以上,即可实现手机云控电脑锁屏。
手机端效果图

PC 端效果图

最后来个效果视频
【原创工具】简单实现云控Win电脑锁屏的更多相关文章
- win7设置电脑锁屏时间
方法/步骤 1 小编用的win7电脑,进入控制面板先~ 2 选择系统与安全选项. 3 如图所示,箭头所指,可以设置锁屏时间,不过电源选项中还有个设置开启屏幕输入密码的设置,第一个就是. 4 如图所示, ...
- stars-one原创工具——蓝奏云批量下载工具
一款可以批量下载蓝奏云分享的文件夹下的所有文件 基于HtmlUnit和okhttp开源库,所以打包后的jar包文件有点大 蓝奏云下载地址 github地址 需求 之前找电子书资源的时候,网友分享的蓝奏 ...
- Xshell4连接Linux后 win快捷键锁屏
今天在使用Xshell连接CentOS后 使用Vim编辑器编辑完后 习惯性的按了Ctrl+S 然后按什么都不起作用 只能重新连接 通过查资料得知 Ctrl + S 是Linux 锁屏的快捷键 要解除锁 ...
- 电脑蓝屏分析教程,附工具WinDbg(x86 x64)6.12.0002.633下载
我们常常在使用电脑中,有时会碰到电脑蓝屏,我们经常束手无策,不知道为什么会蓝屏?有些蓝屏后自动重启能正常进入系统,那么我们就可以借助工具进行分析.而有些可能需要进入到安全模式或者pe系统才会正常,那么 ...
- 怎样在Android实现桌面清理内存简单Widget小控件
怎样在Android实现桌面清理内存简单Widget小控件 我们常常会看到类似于360.金山手机卫士一类的软件会带一个widget小控件,显示在桌面上,上面会显示现有内存大小,然后会带一个按键功能来一 ...
- QM[中控群控云控]01. 中控原理
QM[中控群控云控]01. 中控原理 虽然自己对于中控有了解 也自己做过一些简单中控 不过看看紫猫老师的思路 应该有很多帮助和提高 中控核心:数据交流而已 脚本端和服务端之间的信息交换 ------个 ...
- 【转载】JMeter学习(一)工具简单介绍
JMeter学习(一)工具简单介绍 一.JMeter 介绍 Apache JMeter是100%纯JAVA桌面应用程序,被设计为用于测试客户端/服务端结构的软件(例如web应用程序).它可以用来测试静 ...
- 百度地图API示例之添加/删除工具条、比例尺控件
代码 <!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" cont ...
- 将ipad作为电脑拓展屏或分屏的简单方法
用Ipad实现电脑分屏的方法是挺简单的,但鉴于部分小白找不到合适的门路,在此重新分享一下. 需要的装备: ipad 电脑 数据连接线 方法:某宝上搜索 duet display ,只需1元左 ...
- [转]VS2015 Git 源码管理工具简单入门
VS2015 Git 源码管理工具简单入门 1.VS Git插件 1.1 环境 VS2015+GitLab 1.2 Git操作过程图解 1.3 常见名词解释 拉取(Pull):将远程版本库合并到本 ...
随机推荐
- 解决get请求特殊字符问题
@Bean public ServletWebServerFactory webServerFactory() { TomcatServletWebServerFactory fa = new Tom ...
- Marmoset Toolbag 4.02 八猴渲染器破解版 免费下载
猴安装包下载链接 https://pan.baidu.com/s/1Mgy3Mrlrb3Tvtc8w7Zn1nA?pwd=6666 提取码:6666 Marmoset Toolbag是由Monkey公 ...
- 【HUST】论于渊《Orange‘s:一个操作系统的实现》第三章中PagingDemoProc的必要性,是否可以直接调用LinearAddrDemo?
相关代码如下(第三章pmtest9a.asm改写): LinearAddrDemo equ 00401000h ProcHust equ 00401000h ProcIS19 equ 00501000 ...
- VS Code 插件 clangd的用法
VS Code 插件 clangd的用法 目录 深入理解 VS Code 插件 clangd 的用法 1. clangd 的核心原理:语言服务器协议 (LSP) 与 Clang 分析 关键点: 2. ...
- Data aggregation and group operations in pandas
Data aggregation and group operations in pandas After loading,merging and preparing a dataset,you ma ...
- L1-3、Promp常见任务类型与写法模板
--掌握任务类型,写 Prompt 就像套模板 想把 AI 当成好用的工具,第一步不是写 Prompt,而是识别任务类型.只有你先知道"我到底要它干嘛",才能说出"它该怎 ...
- Django Web应用开发实战第十一章
一.会话控制 Django内置的会话控制简称为session,可以为用户提供基础的数据存储. 数据主要存储在服务器上,并且网站上的任意站点都能使用会话数据. 当用户第一次访问网站时,网站的服务器将自动 ...
- 2024牛客多校3J Rigged Games
欢迎来我的博客看这篇题解! Problem 在两人竞技比赛中,对于任何正整数 \(a\) ,我们定义 \(BO(2 a-1)\) 如下:两名玩家继续竞争,直到其中一人获胜 \(a\) 次,那么他赢得整 ...
- CSP2020复赛后日志
day0 死磕T1,1h30min后估30pts无从下手优化: 看到T2,貌似是组合数学,直接\(pass\) 看到T3,想到暴力--线段树--vector存展开指令--1h30min写完暴力继续T1 ...
- 「Log」2023.8.16 小记
序幕 早上昏迷,九点才到校,少听了四道题,问题不大. 点咖啡喝. SAM 题也抽象.线段树合并,不会. 写个 AC 自动机板子. \(\color{royalblue}{P3808\ [模板]AC\ ...