三套 API 都告诉我成功了,浏览器一个字都没收到

我在写一个跨 Windows 的浏览器自动化脚本。往页面里输一段中文,是里面最基础的一步------打开编辑器,定位输入框,把字敲进去。

这件事我在三层 API 上各试了一遍。

每一层都告诉我成功了。浏览器一个字都没收到。

先说怎么排除的

因为「没输入成功」这种结论太容易骗人了,我先把能排除的全排掉。

第一,前台窗口是不是靶场?读系统句柄:SETFG=True FG=9439172 TARGET=9439172 MATCH,桌面是可交互的。窗口对得上。

第二,焦点是不是落在输入框上?每次实验前读 document.activeElement,全程是 t。焦点也对。

第三,会不会是事件发去了别的标签页?所以我不用输入框监听,改成在 window 上挂捕获阶段的 keydown。只要事件进了浏览器,window 一定记录得到。

三项都排掉之后,观测口径才立住:返回值、window 收到几个 keydown、输入框的值。

第一层:操作系统

SendInput 配 KEYEVENTF_UNICODE,往输入框里打「中文测试」。

四个字符,每个字符发按下和抬起,一共八个事件。函数返回 8。

八个,全部投递成功,没有一个失败。

然后浏览器那边:keydown 收到 0 个,输入框是空的。

我一开始以为这是 KEYEVENTF_UNICODE 的问题------听说 Chromium 会丢弃合成的 Unicode 按键。所以我又做了对照:换成剪贴板 Ctrl+V,走完全不同的通道。

Ctrl+V 的 SendInput 返回 2,2。

输入框还是空的。

对照组一挂,前面那个归因就站不住了。不是 Unicode 按键被丢弃,是**SendInput 整体送不到 Chromium**。

到这里我停了一下,因为这个结果比「某个 API 有 bug」难受得多。它没有 bug,它忠实地完成了自己的职责:把事件投递进系统输入队列。只是队列这一层,压根不是目标窗口会看的地方。

我顺手查了句柄,确认不是送错了窗口------不是,前面查过了,是对的。

第二层:浏览器调试协议

Input.insertText 一行就进去了。四个中文字符,输入框里是「中文测试」。

但它不产生 keydown。

同一个域里还有另一个方法,Input.dispatchKeyEvent。我拿它敲了个字母,发现一件更绕的事。

rawKeyDown 发了一次。keydown 记到 1 个,输入框是空的。 keyDown 带上 text 再发。keydown 同样是 1 个,输入框里有内容了。

同一个函数名,同一个 Input 域,只是 type 参数不同,出来的语义完全不一样。rawKeyDown 只发按键不写字,keyDown 带上 text 才会写字。

两次调用都没有报错。看返回值,你分不出哪个是哪个。

第三层,以及我没料到的地方

到这里我以为结论已经很清楚了:底层不可靠,往上选一个可靠的用。insertText 稳,就它了。

然后我想到一个很自然的问题:我凭什么确定它成功了?

之前每次验证,我看的是输入框的值或者字数计数器。但更省事的做法是监听 keydown------有事件进来,说明字进去了。

这个做法在 insertText 上必然失效。因为 insertText 根本不产生 keydown。它不是模拟按键,它是直接改 DOM。

唯一真正把中文送进编辑器的这条路,恰恰是唯一一个不会发出按键信号的路。

我用来验证输入成功的那个信号,在最可靠的方案上是缺席的。

这比「返回值不可信」难受。返回值不可信,至少还有个替代品。这里是:验证手段和正确方案在结构上互斥,无论怎么调都调不到一起。

我现在怎么做

写完之后我改了三处。

一是所有文字输入统一走 insertText,不再碰合成按键。中英混排也不用切输入法,中文和英文在它这里没有任何区别。

二是判断成功的标准只有一条:读目标的可观测状态 。输入框的值、字数计数器、正文 DOM。任何一层的返回值都不作为依据------SendInput 返回 8 那次已经替我证过一次了。

三是写完一个输入动作,立刻回读一次。不靠日志,不靠返回值,读目标。

慢。比我原来想的那种「发完就往下走」慢很多。但慢的代价我能看见。

顺便一件事

写实验脚本的时候我踩了一个更小的坑,比上面三个都轻,但很像。

我有个循环,要给页面敲三个字母。我写成了 cdp.py key a、key b、key c,外面套了错误重定向。

key 这个子命令的按键表里只有十个特殊键,字母不在里面。它对 a 抛的是异常,不是「敲了个 a」。

我的重定向把异常吞了。循环跑完,三次调用看起来都执行了,实际上一个字都没发。

等我发现的时候,页面上干干净净,没有任何报错痕迹。

同一个病,长得小一点的样子。

写完我回头改了一遍那个实验脚本。最先要改的不是文章,是那句 2>&1。

它让我在错误发生的那一刻就没了声音。我花时间最多的不是查 Chromium为什么不收事件,是查「为什么连报错都看不到」。

相关推荐
2分钟速写快排1 天前
孔乙己的茴字有多少种写法?|AI 对话实时通信的三种实现
前端·javascript·github
用户0044729538251 天前
练歌镜:让眼镜听懂“循环这一句”
javascript
yuyuxun11 天前
协同过滤的校园二手物品智能推荐系统的设计与实现 毕业设计-附源码40082
javascript·vue.js·spring boot·python·数据分析·django·课程设计
总要学点什么1 天前
第105篇:三维特效-流动图片墙体效果——让箭头沿墙长滚,它俩竟是同一份代码
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
Agent世界1 天前
用 CDP 做发布自动化:四道机器闸门与产物验收
前端·javascript
总要学点什么2 天前
第104篇:三维特效-流动墙体效果——让贴图沿墙流动,它却只肯走一条对角线
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
程序员大阳2 天前
副队长ES6+教程(3)--解构赋值
前端·javascript·es6·赋值·解构
神膘护体小月半2 天前
css - 元素背景图由 hover 触发,初次加载时闪动的解决办法
前端·javascript·css
程序员大阳2 天前
副队长ES6+教程(2)--let 与 const
前端·javascript·es6·const·let·var
曦樂~2 天前
【Web Development】03JavaScript 基础和事件
开发语言·前端·javascript