ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

C/C++ 锁定鼠标键盘的多种方法:从 windows.h 到 BlockInput 的代码实现与 TaoToken 配置

C/C++ 锁定鼠标键盘的多种方法:从 windows.h 到 BlockInput 的代码实现与 TaoToken 配置 1. 为什么要在 Windows 上锁定鼠标键盘如果你写过全屏演示工具、考试客户端、Kiosk 一体机程序或者做过自动化测试里的「防误触」环节大概率会遇到一个需求让鼠标别乱跑、让键盘别乱按。Windows 平台下用 C/C非 MFC实现这件事核心就是windows.h里那几个 API——SetCursorPos、ClipCursor、BlockInput。它们看起来都能「锁」但适用边界差别很大有的只限制位置不限制点击有的能限制点击但拦不住键盘有的干脆把鼠标键盘一起冻住。这篇就按控制台工程的场景把三条路径的代码骨架、编译配置、验证动作一次讲清楚。同时我会把 TaoToken 的统一 Key/API 通道配置也串进来——因为你在调试这类系统级调用时经常需要让 AI 帮你补全 API 签名、排查GetProcAddress失败或者生成测试用例用统一通道能省掉反复切工具的成本。适合谁正在写 Windows 控制台程序、需要临时或长期限制输入设备的 C/C 开发者以及想快速跑通可运行示例的人。先说结论方便你对号入座API限制移动限制点击限制键盘需要死循环解除方式SetCursorPos是否否是停止循环ClipCursor是否否否ClipCursor(NULL)BlockInput是是是否BlockInput(FALSE)下面逐个拆。2. TaoToken 前置统一 Key 与 API 通道在动手写代码之前先把调试环境里的 AI 通道配好。TaoToken 提供统一的 Key 和 API 入口官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 。它的作用是你用一个 Key 就能走通模型对话、代码补全、Agent 编码等场景不用为每个工具单独配一套凭证。对这篇的场景来说最实用的两个入口是模型对话https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 用来问BlockInput的函数指针声明为什么编译不过、ClipCursor返回 FALSE 怎么查。Coding Planhttps://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 适合长期写这类系统级代码、需要 Agent 帮你补全工程配置的场景。拿到 Key 之后在控制台工程里你不需要改代码只需要在编辑器的 AI 插件配置里填好端点。下面给一个通用的settings.json片段字段名按你实际用的插件微调即可。{ ai.provider: taotoken, ai.baseUrl: https://taotoken.net/api, ai.apiKey: sk-你的Key, ai.model: claude-sonnet, ai.timeoutMs: 60000, ai.retry: 2 }注意baseUrl只写到/api不要自己拼/v1/chat/completions之类的路径具体路径由插件按协议补全。Key 不要提交到 Git放进环境变量或本地未跟踪的配置文件。配置完做一次验证在编辑器里问一句「ClipCursor的参数类型是什么」能正常返回就说明通道通了。这一步别跳过后面排查GetProcAddress失败时你会用到它。3. 可复制配置控制台工程与三套代码骨架3.1 控制台工程配置用 Visual Studio 建「空项目」→ 添加.cpp文件即可不需要 MFC。关键点只有两个字符集和链接库。字符集项目属性 → 高级 → 字符集选「使用多字节字符集」或「Unicode」都行本文代码不依赖TCHAR。链接库SetCursorPos、ClipCursor在user32.libBlockInput也在user32.lib。VS 默认已链接若用 MinGW 手动加-luser32。命令行编译MinGW 示例g lock_demo.cpp -o lock_demo.exe -luser323.2 SetCursorPos限制位置不限制点击这个最简单但缺点也最明显——必须靠死循环不断把光标拉回来而且点击照样生效。#include windows.h void lock_cursor(void) { SetCursorPos(0, 0); } int main(void) { while (1) { lock_cursor(); Sleep(1); /* 不加 Sleep 会吃满一个核 */ } return 0; }C 版本只是把void lock_cursor(void)换成void lock_cursor()main返回类型一致逻辑不变。实测下来Sleep(1)是必要的否则 CPU 占用会飙到 25% 以上单核满载。3.3 ClipCursor限制位置推荐方案ClipCursor把光标限制在一个矩形区域内不需要死循环还能随时解除。这是日常最推荐的方案。#include stdio.h #include windows.h int clip(int lockb) { RECT rect {0}; rect.bottom 1; rect.right 1; if (lockb) { return ClipCursor(rect); } return ClipCursor(NULL); /* 传 NULL 解除限制 */ } int main(void) { clip(1); printf(鼠标已被限制在左上角 1x1 区域\n); Sleep(2000); clip(0); printf(已解除限制\n); return 0; }C 版本把int lockb改成bool lockb falseprintf换成std::cout其余一致。注意RECT必须用结构体实体不能用LPRECT指针去初始化否则rect指向未分配内存ClipCursor会失败。3.4 BlockInput鼠标键盘一起锁BlockInput不在所有 SDK 的导入库里稳定导出所以常见做法是运行时从user32.dll动态取函数地址。这样写兼容性最好。#include stdio.h #include windows.h typedef BOOL (WINAPI *PFN_BlockInput)(BOOL); int lock_input(int lockb) { HMODULE hMod LoadLibraryA(user32.dll); if (!hMod) { printf(加载 user32.dll 失败\n); return -1; } PFN_BlockInput pfn (PFN_BlockInput)GetProcAddress(hMod, BlockInput); if (!pfn) { printf(获取 BlockInput 地址失败\n); FreeLibrary(hMod); return -1; } BOOL ok pfn(lockb ? TRUE : FALSE); FreeLibrary(hMod); return ok ? 0 : -1; } int main(void) { if (lock_input(1) ! 0) { printf(锁定失败\n); return 0; } printf(鼠标键盘已锁定2 秒后释放\n); Sleep(2000); lock_input(0); printf(已释放\n); return 0; }C 版本把typedef换成using PFN_BlockInput BOOL (WINAPI*)(BOOL);输出用std::cout。这里有个坑BlockInput的调用约定是WINAPI即__stdcall如果你写成默认的__cdecl在 32 位下栈会失衡程序直接崩。64 位下调用约定统一问题不明显但建议始终写WINAPI。4. 验证请求与成功结果代码跑起来只是第一步得确认调用真的生效。分三步验证。第一步编译无警告。用g -Wall或 VS 的/W4重点看有没有「函数指针类型不匹配」的警告这类警告往往就是调用约定写错。第二步运行观察。ClipCursor方案运行后把鼠标往屏幕右下角猛推光标应该停在左上角 1x1 区域内不动2 秒后打印「已解除限制」光标恢复自由。BlockInput方案运行后鼠标完全不动、键盘敲了没反应2 秒后恢复。第三步用返回值判断。ClipCursor成功返回非零失败返回零BlockInput成功返回非零。建议在代码里把返回值打出来BOOL r ClipCursor(rect); printf(ClipCursor 返回: %d, 错误码: %lu\n, r, GetLastError());如果返回 0用GetLastError()拿错误码。常见的是ERROR_ACCESS_DENIED (5)说明当前进程权限不够或者有更高权限的窗口在前台。提示BlockInput在部分系统上需要进程处于前台且未被 UAC 隔离否则调用会静默失败。测试时用普通权限运行即可别一上来就管理员。5. 本篇常见错排查编译报错BlockInput未声明。说明你的 SDK 头文件没导出这个符号别去改头文件直接用第 3.4 节的GetProcAddress动态加载方案。ClipCursor返回 0 且错误码 5。权限或前台窗口问题。先确认程序窗口是前台再检查是否有安全软件拦截。CtrlAltDelete 会强制解除ClipCursor和BlockInput这是系统行为无法绕过测试时别按。SetCursorPos方案 CPU 飙高。忘了加Sleep。死循环里必须让出时间片Sleep(1)起步。GetProcAddress返回 NULL。先确认LoadLibraryA成功再确认函数名拼写。BlockInput是大小写敏感的写成blockinput就取不到。用 TaoToken 的模型对话入口 https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 问一下导出名比翻文档快。32 位下程序崩溃。九成是调用约定写错。BlockInput必须WINAPIClipCursor、SetCursorPos用默认即可。锁定后自己也没法操作只能重启。这是最尴尬的。建议所有锁定代码都配一个定时释放或者用SetTimer做兜底。别写「锁定后等用户按键释放」——键盘都锁了用户按不了。6. 继续用 TaoToken 跑通你的工程把上面的代码放进控制台工程编译、运行、验证整条链路就通了。如果你要长期维护这类系统级代码建议把 AI 通道固定下来接入和排障走 API Keys 加接入文档 https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 验证模型行为走模型对话长期编码和 Agent 任务走 Coding Plan https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 。控制台里查 Key 和管理额度在 console https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 需要看接口细节在 doc https://taotoken.net/api?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。最后留一个我踩过的坑ClipCursor的矩形如果设成{0,0,0,0}等于把光标锁在一个点上某些系统上会导致光标「粘住」无法解除必须传NULL才行。所以解除时永远用ClipCursor(NULL)别用空矩形。
返回列表