恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

C语言模拟键盘鼠标事件:TaoToken统一Key接入与config.toml配置骨架

  • 首页
  • 资讯中心
  • /
  • C语言模拟键盘鼠标事件:TaoToken统一Key接入与config.toml配置骨架

相关资讯

VSCode插件开发流程兼【代码注释阅读小说】插件发布:用TaoToken统一Key打通AI注释生成与发布链路 2026/9/27 22:00:10
效率翻倍!2026 AI 编程工具横评:TaoToken 统一 Key 接入 Cline 与 CC Switch 配置实战 2026/9/27 22:00:10
ClawLink 遇上 TaoToken:给 AI Agent 社交网络配一套跨部门协作的 settings.json 骨架 2026/9/27 22:00:10

最新资讯

CAN总线与菊花链选型实战指南:通信协议决策逻辑
写毛利语论文,别让 AI 替你“懂”那个词:我的分阶段工具清单
汽车电子入门:从STM32到FOC的电机控制实战路线
STM32CubeMX 6.14入门:从下载安装到点灯调试实战指南
1553B 总线方式码与状态字核心概念与实操详解
LSTM时间序列预测实战:数据预处理、模型搭建与滚动预测完整指南

今日推荐

从像素到笔画:srt-whiteboard-animation骨架笔迹追踪实现(Zhang-Suen细化+8邻接追踪)
网站建设的英语怎么说?别只背单词,看完这套安全完整流程才敢上线
新手入门看这篇:建设网站加盟避坑指南与SEO实操

本周热门

从像素到笔画:srt-whiteboard-animation骨架笔迹追踪实现(Zhang-Suen细化+8邻接追踪)
网站建设的英语怎么说?别只背单词,看完这套安全完整流程才敢上线
新手入门看这篇:建设网站加盟避坑指南与SEO实操

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

C语言模拟键盘鼠标事件:TaoToken统一Key接入与config.toml配置骨架

发布时间:2026/9/27 22:00:10
C语言模拟键盘鼠标事件:TaoToken统一Key接入与config.toml配置骨架 1. 从 keybd_event 到 SendInput为什么你的自动化脚本需要一个统一通道如果你写过 Windows 下的 C 语言自动化脚本大概率见过keybd_event和mouse_event这对老搭档。它们确实能用几行代码就能让浏览器自己打开、输入关键词、点下回车。但真把它们放进稍微复杂一点的场景问题就来了keybd_event是上世纪留下的接口微软官方文档里明确建议改用SendInput而当你需要把本地脚本和远端模型能力串起来时又会撞上第二堵墙——每个模型一个 Key、一套鉴权、一种返回格式脚本里到处是硬编码的 URL 和密钥。这篇就聚焦两件事一是用SendInput把键盘鼠标事件注入写扎实二是在不引入任何第三方库的前提下让 C 程序通过一个统一的 Key 通道去调用模型接口。适合正在做本地自动化、RPA 小工具、或者想把脚本接进统一 API 通道的开发者。全程只用 Windows 原生 API 加一个 HTTP 客户端编译环境用 MSVC 或 MinGW 都行。我试过把事件注入和网络请求混在一个main里结果调试时根本分不清是按键没生效还是请求超时。所以下面会拆成清晰的步骤每一步都能单独验证。2. TaoToken 前置准备统一 Key 与 config.toml 骨架TaoToken 在这里扮演的角色是「统一入口」你不需要在 C 代码里为不同模型维护不同的地址和密钥而是通过一个 Key 走同一个 API 端点。官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 端点是 https://taotoken.net/api 。先做两件准备工作。第一拿到 Key。进入控制台创建 API Key地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 创建后复制保存。Key 只在创建时完整显示一次丢了只能重建。第二写配置文件。C 语言读 TOML 没有标准库但 TOML 本身语法简单我们可以写一个极简解析器或者干脆用「键值」逐行读取。下面这份config.toml骨架可以直接复制字段含义写在注释里# config.toml - TaoToken 统一接入配置骨架 [api] # 统一 API 端点不要带末尾斜杠 base_url https://taotoken.net/api # 从控制台创建的 Key形如 sk-xxxx api_key sk-替换成你自己的Key # 请求超时单位毫秒 timeout_ms 30000 [model] # 对话模型标识按需替换 name claude-sonnet # 单次最大输出 token max_tokens 1024 # 采样温度 temperature 0.7 [input] # 模拟按键之间的间隔单位毫秒太快会被目标程序丢弃 key_delay_ms 120 # 鼠标移动后到点击的等待时间 click_delay_ms 200这里有个容易踩的坑base_url末尾不要加斜杠否则拼接/v1/messages之类的路径时会出现双斜杠部分网关会直接返回 404。另外api_key不要提交到 Git建议在.gitignore里加上config.toml。如果你后续要做长期编码或 Agent 类任务可以了解 Coding Plan地址是 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它更适合持续性的开发场景而本篇这种「脚本触发一次请求」的用法用普通 API Key 就够了。3. 可复制配置SendInput 事件注入 配置读取3.1 用 SendInput 替代 keybd_eventSendInput的核心是构造INPUT结构体数组一次性提交。相比keybd_event它能更精确地控制扫描码、支持 Unicode 输入、并且返回实际注入的事件数量方便做错误判断。先看键盘部分。下面这个函数把「按下并松开一个虚拟键」封装起来#include windows.h #include stdio.h // 模拟一次按键按下 松开 // vk: 虚拟键码例如 A、VK_RETURN static int send_key(WORD vk, DWORD delay_ms) { INPUT inputs[2]; ZeroMemory(inputs, sizeof(inputs)); // 按下 inputs[0].type INPUT_KEYBOARD; inputs[0].ki.wVk vk; inputs[0].ki.wScan 0; inputs[0].ki.dwFlags 0; // 松开 inputs[1].type INPUT_KEYBOARD; inputs[1].ki.wVk vk; inputs[1].ki.wScan 0; inputs[1].ki.dwFlags KEYEVENTF_KEYUP; UINT sent SendInput(2, inputs, sizeof(INPUT)); if (sent ! 2) { // 返回实际注入数量不等于 2 说明被拦截或参数错误 return -1; } Sleep(delay_ms); return 0; }鼠标部分同理用INPUT_MOUSE类型。移动用MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE点击用MOUSEEVENTF_LEFTDOWN和MOUSEEVENTF_LEFTUP// 绝对坐标移动 左键单击 // x, y 为屏幕绝对坐标0~65535 归一化前需换算 static int send_click(int x, int y, DWORD delay_ms) { // 屏幕尺寸换算成 0~65535 int screen_w GetSystemMetrics(SM_CXSCREEN); int screen_h GetSystemMetrics(SM_CYSCREEN); LONG nx (LONG)((double)x * 65535 / (screen_w - 1)); LONG ny (LONG)((double)y * 65535 / (screen_h - 1)); INPUT inputs[3]; ZeroMemory(inputs, sizeof(inputs)); inputs[0].type INPUT_MOUSE; inputs[0].mi.dx nx; inputs[0].mi.dy ny; inputs[0].mi.dwFlags MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE; inputs[1].type INPUT_MOUSE; inputs[1].mi.dwFlags MOUSEEVENTF_LEFTDOWN; inputs[2].type INPUT_MOUSE; inputs[2].mi.dwFlags MOUSEEVENTF_LEFTUP; UINT sent SendInput(3, inputs, sizeof(INPUT)); if (sent ! 3) { return -1; } Sleep(delay_ms); return 0; }注意坐标归一化SendInput的绝对坐标是 0 到 65535 的映射值不是像素。如果你直接传像素值鼠标会跑到屏幕角落。上面用GetSystemMetrics做了换算这是最稳的写法。3.2 读取 config.toml 的极简实现不引入第三方库就按行读、按切分。下面这个函数把配置读进一个结构体typedef struct { char base_url[256]; char api_key[256]; char model[64]; int timeout_ms; int key_delay_ms; int click_delay_ms; } AppConfig; // 去掉首尾空白 static void trim(char *s) { char *p s; while (*p || *p \t) p; if (p ! s) memmove(s, p, strlen(p) 1); size_t len strlen(s); while (len 0 (s[len-1] || s[len-1] \t || s[len-1] \r || s[len-1] \n)) { s[--len] \0; } } static int load_config(const char *path, AppConfig *cfg) { FILE *fp fopen(path, r); if (!fp) return -1; char line[512]; while (fgets(line, sizeof(line), fp)) { trim(line); if (line[0] # || line[0] [ || line[0] \0) continue; char *eq strchr(line, ); if (!eq) continue; *eq \0; char *key line; char *val eq 1; trim(key); trim(val); // 去掉值两端的引号 size_t vlen strlen(val); if (vlen 2 val[0] val[vlen-1] ) { val[vlen-1] \0; val; } if (strcmp(key, base_url) 0) strncpy(cfg-base_url, val, 255); else if (strcmp(key, api_key) 0) strncpy(cfg-api_key, val, 255); else if (strcmp(key, model) 0) strncpy(cfg-model, val, 63); else if (strcmp(key, timeout_ms) 0) cfg-timeout_ms atoi(val); else if (strcmp(key, key_delay_ms) 0) cfg-key_delay_ms atoi(val); else if (strcmp(key, click_delay_ms) 0) cfg-click_delay_ms atoi(val); } fclose(fp); return 0; }这个解析器不处理多行数组、内联表等复杂 TOML 语法但对上面那份骨架完全够用。如果你以后要扩展建议换成tomlc99这类单文件库仍然不违反「不依赖重型第三方库」的原则。4. 验证请求一次模拟按键触发 返回码检查现在把事件注入和通道调用串起来。思路是脚本先模拟按键打开一个输入框并输入内容然后把这个内容作为 prompt 发给统一 API最后检查 HTTP 返回码和响应体。4.1 用 WinHTTP 发请求Windows 自带 WinHTTP不需要额外链接库链接时加-lwinhttp。下面是一个最小 POST 封装#include winhttp.h #pragma comment(lib, winhttp.lib) // 返回 HTTP 状态码响应体写入 resp_buf static int http_post_json(const AppConfig *cfg, const char *body, char *resp_buf, size_t resp_size) { int status -1; HINTERNET hSession NULL, hConnect NULL, hRequest NULL; // 解析主机名和路径这里假设 base_url 为 https://taotoken.net/api hSession WinHttpOpen(LC-Auto/1.0, WINHTTP_ACCESS_TYPE_DEFAULT_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); if (!hSession) goto cleanup; hConnect WinHttpConnect(hSession, Ltaotoken.net, INTERNET_DEFAULT_HTTPS_PORT, 0); if (!hConnect) goto cleanup; hRequest WinHttpOpenRequest(hConnect, LPOST, L/api/v1/messages, NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_SECURE); if (!hRequest) goto cleanup; // 组装请求头 wchar_t headers[512]; swprintf(headers, 512, LContent-Type: application/json\r\n LAuthorization: Bearer %hs\r\n, cfg-api_key); if (!WinHttpSendRequest(hRequest, headers, -1L, (LPVOID)body, (DWORD)strlen(body), (DWORD)strlen(body), 0)) { goto cleanup; } if (!WinHttpReceiveResponse(hRequest, NULL)) goto cleanup; DWORD code 0, len sizeof(code); WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, WINHTTP_HEADER_NAME_BY_INDEX, code, len, WINHTTP_NO_HEADER_INDEX); status (int)code; // 读响应体 DWORD read 0, total 0; while (WinHttpReadData(hRequest, resp_buf total, (DWORD)(resp_size - total - 1), read) read 0) { total read; if (total resp_size - 1) break; } resp_buf[total] \0; cleanup: if (hRequest) WinHttpCloseHandle(hRequest); if (hConnect) WinHttpCloseHandle(hConnect); if (hSession) WinHttpCloseHandle(hSession); return status; }4.2 主流程按键触发 请求 返回码验证int main(void) { AppConfig cfg; memset(cfg, 0, sizeof(cfg)); if (load_config(config.toml, cfg) ! 0) { printf(config.toml 读取失败\n); return 1; } // 1. 模拟按键输入 hello 并回车 const WORD keys[] { H, E, L, L, O, VK_RETURN }; for (int i 0; i 6; i) { if (send_key(keys[i], cfg.key_delay_ms) ! 0) { printf(按键注入失败索引 %d\n, i); return 2; } } // 2. 模拟鼠标点击 (400, 300) if (send_click(400, 300, cfg.click_delay_ms) ! 0) { printf(鼠标注入失败\n); return 3; } // 3. 调用统一 API const char *body {\model\:\claude-sonnet\, \max_tokens\:64, \messages\:[{\role\:\user\,\content\:\ping\}]}; char resp[4096]; int status http_post_json(cfg, body, resp, sizeof(resp)); printf(HTTP 状态码: %d\n, status); printf(响应片段: %.200s\n, resp); if (status 200) { printf(通道调用成功\n); } else { printf(通道调用失败请检查 Key 与端点\n); } return 0; }编译命令MinGW 为例gcc main.c -o auto.exe -lwinhttp -luser32运行后你应该看到类似输出HTTP 状态码: 200 响应片段: {id:msg_...,content:[{type:text,text:pong}]} 通道调用成功如果状态码是 200说明事件注入和统一通道都跑通了。如果按键没生效但状态码正常问题在事件注入侧如果状态码是 401 或 404问题在配置侧。5. 本篇常见错排查5.1 SendInput 返回 0 或小于预期最常见的原因是权限。如果你的目标程序以管理员身份运行而你的脚本是普通权限SendInput会被 UIPI用户界面特权隔离拦截返回 0。解决办法是让脚本和目标程序权限一致或者用管理员权限运行脚本。另一个原因是INPUT结构体没有ZeroMemory残留的dwFlags导致事件类型错乱。上面代码里每次都清零就是为了避开这个坑。5.2 鼠标点击位置偏移如果你传的是像素坐标而没做 0~65535 归一化鼠标会跑到屏幕边缘。检查send_click里的换算公式确认用了GetSystemMetrics(SM_CXSCREEN)。另外多显示器环境下SM_CXSCREEN只返回主屏宽度跨屏点击需要改用MOUSEEVENTF_VIRTUALDESK。5.3 HTTP 返回 401Authorization头格式是Bearer sk-xxxx注意Bearer和 Key 之间有一个空格。如果 Key 是从控制台复制的确认没有把首尾空格带进去。另外检查config.toml里api_key的引号是否被正确去掉——上面的解析器会去掉成对引号但如果你手写时漏了引号值里可能混入注释。5.4 HTTP 返回 404大概率是base_url末尾多了斜杠或者请求路径拼错。本篇用的路径是/api/v1/messages如果你的端点不同以接入文档为准。文档地址是 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的路径和参数说明。5.5 编译报错找不到 winhttp.hMinGW 需要安装mingw-w64的完整包MSVC 则默认自带。链接时别忘了-lwinhttp。如果用的是旧版 MinGW可能还需要-lole32。6. 把事件注入和统一通道拆开维护走到这里你已经有了一个能跑的最小闭环SendInput负责本地事件config.toml负责配置WinHTTP 负责通道调用。实际项目里我建议把这三块拆成独立文件——input.c只管按键鼠标config.c只管读配置api.c只管网络请求。这样调试时你可以单独替换某一块而不用在几百行main里找问题。如果你接下来要验证不同模型的返回差异可以直接用模型对话页面快速对比地址是 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodelsutm_campaignrewrite 不用每次都改 C 代码重新编译。而当你需要管理多个 Key、查看调用量时控制台里的 API Keys 页面会更方便https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。最后提醒一句SendInput注入的事件会进入真实的系统输入队列调试时把key_delay_ms调大一点给自己留出按CtrlC的时间否则脚本可能会在你没准备好的窗口里乱点。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号