浏览器说“没有处理程序”,它到底没找到什么?
一次 tarscode:// 源码跳转排错:先找准验证路径,再判断修复是否有效
01 问题很小,判断下得太快
我给一张 draw.io 流程图加了几百个源码链接。点一下节点,就应该在本地 Qoder 打开对应文件,跳到对应代码行。为此,我做了一个 tarscode:// 自定义协议:本地脚本接收链接,解析源码标识,再调用编辑器。
但从 Edge 点击时,弹出来的是空白标签,控制台提示:
Failed to launch 'tarscode://open/launcher-config'
because the scheme does not have a registered handler.
前面的排查却先后给出了不少“正常”结果:直接执行链接可以打开程序,关联 API 能返回信息,注册表里也看得到登记。我很快认定:注册没问题,是浏览器的问题。后面绕的很多路,都从这个判断开始。
真正需要解释的,不是“为什么所有测试都通过还失败”,而是:这些测试究竟分别证明了什么?
02 三种检查,不能互相替代
最先走偏的是默认应用设置。我花了不少时间检查 UserChoice、Capabilities 和 RegisteredApplications,想让协议出现在设置界面里。后来,同机可用的 qoder-cn 成了最有价值的对照:它没有我试图补齐的那套默认应用配置,却仍能被正常解析。
这至少说明,在这台机器和这个场景里,“必须先出现在默认应用界面”不是成立的前提。随后我发现,连测试方法也需要拆开来看。

图 1:检查结果只能证明它覆盖的那条路径。
命令行能启动,说明那条启动路径可用;API 查询有结果,说明指定参数下查到了某项信息;从图中点击后打开正确文件和行号,才覆盖了实际使用入口。把它们统称为“注册正常”,会把关键差异藏掉。
还有一次重新检查时,tarscode 的登记已经不存在,查询返回无关联。之前“看得到”和现在“找不到”发生在不同时间,不能拼成一张同时成立的状态图。多轮安装、修改和清理之后,每次都要重新确认当前状态。
03 回到源码,核对名称解析路径
所查的 Chromium 实现会检查最终取得的 program_name。它为空时,相关分支会输出前面的错误。这里用源码解释机制;由于没有对齐故障当时的 Edge 版本,它还不能独自证明本机根因。下面只摘取关键判断,不代表整个外部协议处理流程。[1]
// 关键判断摘录:不是整个启动流程
if (program_name.empty()) {
// 输出 scheme does not have a registered handler
return;
}
Windows 侧先带 ASSOCF_IS_PROTOCOL 查询 ASSOCSTR_FRIENDLYAPPNAME;查询没有得到名称时,还会回退到注册表。协议默认值、启动命令中的程序文件名,都可能成为最终返回的名称。[2]

图 2:友好名为空不等于最终名称为空,回退路径不能省略。
所以,“FRIENDLYAPPNAME 查不到”不能直接推出“浏览器必然报错”。它是排查线索,而不是完整结论。这也解释了为什么探针应该尽量贴近目标路径,却仍不能代替浏览器本身。
报错文案给的是入口。真正需要核对的是:代码在哪个分支、检查什么值、失败后还有没有回退。
04 查询恢复,不等于单一根因已经证明
当时,我用同一组协议查询条件做了对照,记录如下。这是本机排错记录,不是各个协议在所有 Windows 环境中的固定表现。
codex 的两项结果不同,说明它们必须分别观察,但不能据此认定关联损坏。Windows 文档明确指出,并非所有应用关联都有可执行文件。[3]
恢复 tarscode 登记后,还出现过“可执行文件有结果、友好名称没有结果”的状态。补充 Application\ApplicationName 后,本机探针恢复正常。最终保留的注册结构如下:
# 注册结构示意,不是可直接导入的 .reg 文件
HKCU\Software\Classes\tarscode
(默认) = URL:tarscode
URL Protocol = 空字符串
shell\open\command
(默认) = 带引号的启动程序、脚本路径和 URL 参数
Application
ApplicationName = TARS 源码跳转 (tarscode)
这里我保留两个不同层次的结论:补充这项后,当时的查询恢复了;但不能因此把“pythonw 缺少文件说明”写成唯一根因。名称解析还有回退,如果同一时刻已有有效默认值和启动命令,就需要结合当时的浏览器版本与实际读取状态解释,不能跳过这一段。
这次修改恢复了本机探针的查询结果,但不能推出“所有 pythonw 协议都必须这样注册”。
05 把探针放到点击之前
为了减少下次猜测,我给工具增加了 --probe,分别输出可执行文件与友好名称查询结果。关键是显式使用协议查询标志,并保留 HRESULT,而不是只输出一句“正常”。下面展示探针的核心调用,运行环境是 Windows。[4]
// 每个查询项都重新准备缓冲区和长度
for (ASSOCSTR field : {ASSOCSTR_EXECUTABLE,
ASSOCSTR_FRIENDLYAPPNAME}) {
wchar_t value[1024] = {};
DWORD length = 1024;
HRESULT hr = AssocQueryStringW(
ASSOCF_IS_PROTOCOL, field, L"tarscode",
nullptr, value, &length);
// 记录 field、hr;成功时再记录 value
// 若缓冲区不足,按返回的 length 扩容后重试
}
这段是核心片段,不是完整程序;它只查两项关联信息,没有复现图 2 的 URL Protocol 标记校验和注册表回退。两项皆通过是这套 tarscode 工具自定的检查门槛,不是 Windows 的通用规则;即使通过,仍要验证真实点击。
我还保留了一条 HTTP 回环路线:打开图时在 127.0.0.1 启动临时服务,只改内存中图副本的链接,磁盘原图不动。它绕开了自定义协议关联,但仍依赖服务可用、端口可达和浏览器允许访问。它的意义是换一条故障路径,而不是承诺永不失败。
06 从图里的链接,重新走完一次

图 3:每条日志只证明一个阶段,最终目标是正确的文件和行号。
现有复盘记录明确写到了探针恢复,但没有列出两条路线各自的真实点击结果。下面是最终验收的标准,不把它当成已经完成的验收报告:从浏览器里的流程图点击,检查这次点击对应的日志,再确认编辑器打开了正确的文件和行号。
started、resolved、qoder_dispatched 和 http_received 是这个本地工具的日志标记,不是浏览器或 Qoder 的标准日志。“请求已收到”“已经完成解析”“已经发给编辑器”,各自证明的范围不同;派发日志也不能代替对正确文件和行号的确认。
这次最浪费时间的,是太早认定故障在哪,又用不对应的测试支持这个判断。以后再遇到“明明配置了,却说找不到”,我会先找一个当前可用的对照,确认目标系统实际走的查询路径,再从真实入口完整验证一次。
继续修改之前,先问清楚:它到底在找什么,而我刚才证明的,又是什么?