其他
用 CLRLoad 日志诊断 .NET Framework…
当 .NET Framework 进程出现运行时选择错误、无法加载 CLR、意外/缺失 .NET 3.5 FOD 弹窗或 v2/v4 同进程加载时,通过分析 mscoree.dll 生成的 CLRLoad 激活日志定位 shim 的决策路径。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 48/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 53/100 ×0.15
适用场景
用 CLRLoad 日志诊断 .NET 通用
解决思路
- 先启用 CLR 激活日志:设置环境变量 `set COMPLUS_CLRLoadLogDir=C:\CLRLoadLogs`(推荐,仅当前会话),或设置注册表 `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework` 下的 `CLRLoadLogDir`(REG_SZ,机器级,影响所有 .NET Framework 进程);64 位系统上若涉及 32 位进程,还需在 `Wow6432Node` 下设置。注意日志目录必须已存在,shim 不会自动创建,否则不会写日志也无任何报错。日志文件名为 `{ProcessName}.CLRLoad{NN}.log`(NN=00–99,每个进程实例一个),进程退出前无法读取。抓取完成后移除环境变量或注册表键以停止记录。
- 加载技能自带的参考文件(从包含 SKILL.md 的目录解析路径,不要用工作区搜索):依次读取 `references/log-format.md`(日志行格式与所有已知消息类型)、`references/activation-flow.md`(运行时选择决策树)、`references/com-activation.md`(COM DllGetClassObject 激活与 CLSID 注册表布局)。若某文件读取失败,先列出该技能的 `references/` 目录一次,仅在列表中出现预期文件时重试;若仍缺失,用已加载参考加内联知识补足,并在最终诊断中注明 `Reference coverage: reduced; unavailable: <paths>; used inline guidance for missing references.`
- 先总览日志文件:按进程名分组列出所有日志,对每个进程扫描结果行——`Decided on runtime: vX.Y.Z`(成功)、`ERROR:`(失败)、`Launching feature-on-demand`(已弹 FOD)、`Could have launched feature-on-demand`(本可弹但被抑制)、`V2.0 Capping is preventing consideration`(因 capping 跳过 v4+)。可用 `grep -l "ERROR:\|Launching feature-on-demand\|Could have launched" *.log` 和 `grep -c "Launching feature-on-demand" *.log`,并建立「进程 / 日志文件 / 结果 / 选中运行时 / 是否 FOD」汇总表。
- 对每个异常日志自上而下追踪完整激活流程。注意嵌套/重入日志:shim 内部调用(如 DllGetClassObject 内部调用 ComputeVersionString、FindLatestVersion,或 FOD 检查重跑版本计算)会在同一激活序列中产生第二段 ComputeVersionString 块,不要误判为独立激活。
- 识别入口点:第一条 `FunctionCall:` 或 `MethodCall:` 行表明激活触发方式——`_CorExeMain`(托管 EXE 启动)、`DllGetClassObject. Clsid: {guid}`(COM 激活)、`ClrCreateInstance`(现代 v4+ 宿主 API)、`CorBindToRuntimeEx`(旧版 v1/v2 宿主 API,将进程绑定到单一运行时)、`ICLRMetaHostPolicy::GetRequestedRuntime`(基于策略的宿主 API)、`LoadLibraryShim`(按名加载框架 DLL 的旧版 API)。
- 检查输入参数:入口点之后日志会输出——`IsLegacyBind`(是否为 v4 之前的旧版激活路径,旧版 API 会置 1)、`IsCapped`(为 1 时 roll-forward 被限制在 Whidbey v2.0.50727,枚举已安装运行时时不会考虑 v4.0+;但显式传入 v4 版本字符串或配置 `useLegacyV2RuntimeActivationPolicy` 时仍可加载 v4+)、`SkuCheckFlags`、`ShouldEmulateExeLaunch`、`LegacyBindRequired`。
- 检查配置文件处理:`Parsing config file: {path}`、`Config File (Open). Result:00000000`(找到并打开)、`Config File (Open). Result:80070002`(未找到,ERROR_FILE_NOT_FOUND)、`Found config file: {path}`、`UseLegacyV2RuntimeActivationPolicy is set to {0|1}`(为 1 时所有运行时都成为旧版代码路径的候选,可枚举并# 选择 v4+,副作用是关闭进程内 SxS 并把 v4 之前的运行时锁在进程外)、`Config file includes SupportedRuntime entry. Version: vX.Y.Z, SKU: {sku}`。关键点:无配置文件且做 capped 旧版绑定时,shim 没有任何东西把它引向 v4.0,会枚举(上限 ≤v2.0)已安装运行时,若未装 3.5 则一无所获并失败——这是有意设计,让 v4 安装对旧版路径无影响。
- 检查版本解析:`Installed Runtime: vX.Y.Z. VERSION_ARCHITECTURE: N`、`{exe} was built with version: vX.Y.Z`(来自 PE 头,仅托管程序集)、`Using supportedRuntime: vX.Y.Z`、`FindLatestVersion is returning the following version: vX.Y.Z ... V2.0 Capped: {0|1}`、`Default version of the runtime on the machine: vX.Y.Z` 或 `(null)`(null 表示什么都没找到)、`Decided on runtime: vX.Y.Z`(最终决定)。
- 检查失败与 FOD 路径:`ERROR: Unable to find a version of the runtime to use`;`SEM_FAILCRITICALERRORS is set to {value}`(0 表示允许错误对话框和 FOD,非零如 0x8001 表示抑制,该标志从父进程继承);`Checking if feature-on-demand installation would help`(重跑版本计算判断装 .NET 3.5 是否有帮助);随后是 `Launching feature-on-demand installation. CmdLine: "...\fondue.exe" /enable-feature:NetFx3`(已弹 FOD)或 `Could have launched feature-on-demand installation if was not opted out.`(因设置了 SEM_FAILCRITICALERRORS 被抑制)。
- 检查单进程内多次激活:每个新 `FunctionCall:`/`MethodCall:` 开始一段独立激活序列。常见模式是先经 `ClrCreateInstance`/`GetRequestedRuntime` 成功加载 v4.0,随后 `DllGetClassObject`(COM)走旧版 capped 绑定失败——例如原生 EXE(link.exe、mt.exe)为主工作加载 CLR 后,次级 COM 激活(如 diasymreader)触发另一次找不到 v2.0 的旧版解析。
- 必要时检查系统状态:用 PowerShell 查看 CLSID 注册(`Get-ItemProperty 'Registry::HKCR\CLSID\{guid}'`、其 `InprocServer32` 及版本子键),确认 `(Default)` 为 `mscoree.dll`、版本子键(如 `2.0.50727`、`4.0.30319`)、`ImplementedInThisVersion`、`Assembly`/`Class`、`RuntimeVersion`;用 `Get-ChildItem 'Registry::HKLM\SOFTWARE\Microsoft\.NETFramework\policy'` 查看已安装运行时;SEM_FAILCRITICALERRORS 由父进程继承,若构建系统或脚本调用 SetErrorMode 设置它,所有子进程都会继承。
- 输出诊断报告,覆盖:发生了什么(哪些进程出现激活问题及症状)、为什么发生(追踪 shim 中导致该结果的具体决策路径)、什么控制了该行为(配置文件是否存在、错误模式、CLSID 注册、capping 状态)、以及(若适用)什么发生了变化(父进程错误模式、配置文件、CLSID 注册、已安装运行时)。
- 交付前按清单校验:所有含错误或 FOD 触发的日志都已分析(不只第一个);每个问题激活的入口点已识别;每段激活序列的 capping 与旧版绑定状态已记录;配置文件有无已检查;FOD 相关问题已记录 SEM_FAILCRITICALERRORS 状态;单日志内多次激活已分别追踪;诊断解释了具体决策路径而非仅结果。
适用边界 · 注意事项
- 现代 .NET(CoreCLR / .NET 5+)不适用——本技能只覆盖 .NET Framework(mscoree.dll shim)。
- 程序集绑定失败不适用——应使用 Fusion 日志(fuslogvw.exe),而非 CLR 激活日志。
- CLR 已加载后的运行时崩溃不适用——激活已成功,问题在别处。