clr-activation-debugging
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCLR Activation Debugging
CLR激活调试
Diagnose .NET Framework runtime activation issues by analyzing CLR activation logs (CLRLoad logs) produced by the shim (mscoree.dll). These logs record every decision the shim makes when selecting and loading a CLR version.
通过分析垫片程序(mscoree.dll)生成的CLR激活日志(CLRLoad日志)诊断.NET Framework运行时激活问题。这些日志记录了垫片程序在选择和加载CLR版本时做出的每一个决策。
When to Use
适用场景
- A process fails to load the CLR at all ("Unable to find a version of the runtime to use")
- The shim picks the wrong CLR version (e.g., v2.0 instead of v4.0)
- Unexpected .NET 3.5 Feature-on-Demand (FOD) install dialogs appear
- FOD dialogs are expected but do NOT appear
- Both CLR v2 and CLR v4 load into the same process, causing failures
- A COM object fails to activate because the shim can't resolve the runtime
- Legacy hosting APIs (CorBindToRuntime) bind to an unexpected version
- 进程完全无法加载CLR(提示“找不到要使用的运行时版本”)
- 垫片程序选择了错误的CLR版本(例如选择v2.0而非v4.0)
- 意外弹出.NET 3.5按需功能(FOD)安装对话框
- 本应弹出FOD对话框却未出现
- CLR v2和CLR v4同时加载到同一进程导致故障
- COM对象因垫片程序无法解析运行时激活失败
- 旧版托管API(CorBindToRuntime)绑定到了意外的版本
When Not to Use
不适用场景
- Modern .NET (CoreCLR / .NET 5+) — this skill covers .NET Framework only (the mscoree.dll shim)
- Assembly binding failures — use Fusion logs (fuslogvw.exe), not CLR activation logs
- Runtime crashes after the CLR has loaded — activation succeeded; the problem is elsewhere
- 现代.NET(CoreCLR / .NET 5+) —— 本指南仅适用于.NET Framework(mscoree.dll垫片)
- 程序集绑定失败 —— 请使用Fusion日志(fuslogvw.exe),而非CLR激活日志
- CLR加载完成后的运行时崩溃 —— 此时激活已成功,问题出在其他环节
Background
背景
The Shim Architecture
垫片架构
The .NET Framework shim has two layers:
- mscoree.dll (the "shell shim") — the public-facing DLL that is the registered for CLR-hosted COM objects and the entry point for
InprocServer32, legacy APIs, etc._CorExeMain - mscoreei.dll — the actual shim implementation where the runtime selection logic, logging, and activation decisions live. mscoree.dll forwards into mscoreei.dll.
When reading logs, the in FOD command lines reflects this — it's mscoreei.dll doing the work.
caller-name:mscoreei.dll.NET Framework垫片分为两层:
- mscoree.dll(“外壳垫片”)—— 对外暴露的DLL,是CLR托管COM对象的注册,也是
InprocServer32、旧版API等的入口点_CorExeMain - mscoreei.dll —— 垫片的实际实现,包含运行时选择逻辑、日志记录和激活决策逻辑,mscoree.dll会将请求转发到mscoreei.dll
读取日志时,FOD命令行中的就反映了这一架构 —— 实际执行工作的是mscoreei.dll。
caller-name:mscoreei.dll.NET 3.5 / v2.0.50727 Version Mapping
.NET 3.5 / v2.0.50727版本映射
.NET 2.0, 3.0, and 3.5 all share the same CLR runtime version: v2.0.50727. The "3.0" and "3.5" releases were library additions on top of CLR v2.0. For activation purposes, they are all "v2.0.50727." When the shim resolves to v2.0.50727 or FOD offers to install "NetFx3", it's installing the CLR v2.0 runtime (plus the 3.0/3.5 libraries). Similarly, CLR v4.0 (v4.0.30319) covers all .NET Framework versions from 4.0 through 4.8.x.
.NET 2.0、3.0和3.5都共享同一个CLR运行时版本:v2.0.50727。“3.0”和“3.5”版本是在CLR v2.0基础上新增的类库。从激活的角度来看,它们都属于“v2.0.50727”。当垫片解析到v2.0.50727或者FOD提示安装“NetFx3”时,实际安装的是CLR v2.0运行时(加上3.0/3.5类库)。同理,CLR v4.0(v4.0.30319)覆盖了从4.0到4.8.x的所有.NET Framework版本。
.NET 3.5 Availability on Recent Windows
近期Windows版本上的.NET 3.5可用性
On recent Windows versions (Windows 11 Insider Preview Build 27965 and future platform releases), .NET Framework 3.5 is no longer available as a Windows optional component (Feature-on-Demand). It must be installed from a standalone MSI. This means the FOD dialog () will not succeed on these systems even if it fires. On Windows 10 and Windows 11 through 25H2, FOD remains available. .NET Framework 3.5 reaches end of support on January 9, 2029.
fondue.exe /enable-feature:NetFx3在近期的Windows版本(Windows 11预览体验版本27965及未来的平台版本)中,.NET Framework 3.5不再作为Windows可选组件(按需功能)提供,必须通过独立MSI安装。这意味着即便触发了FOD对话框(),在这些系统上也无法安装成功。在Windows 10和Windows 11 25H2及更早版本中,FOD仍然可用。.NET Framework 3.5的支持将于2029年1月9日终止。
fondue.exe /enable-feature:NetFx3Shim HRESULT Codes
垫片HRESULT码
When the shim fails, it returns specific HRESULTs in the range. These are the errors you'll see from callers (not in the activation logs themselves, which log human-readable messages):
0x8013xxxx| HRESULT | Symbol | Meaning |
|---|---|---|
| | Cannot find or load a suitable runtime version. This is the most common shim error — it's what callers see when capped legacy activation fails on a v4-only machine. |
| | Found a runtime but failed to get a required export or interface from it. |
| | The .NET Framework install root is missing or invalid in the registry. |
| | A required component of the installation is missing. |
| | A different runtime is already bound as the legacy runtime. A legacy API tried to bind to a version that conflicts with the one already chosen. |
| | The shim is shutting down and cannot service the request. |
If a user reports one of these HRESULTs (especially ), CLR activation logs are the right diagnostic tool.
0x80131700当垫片运行失败时,会返回范围内的特定HRESULT。这些是调用方会收到的错误(不会出现在激活日志中,日志中记录的是人类可读的消息):
0x8013xxxx| HRESULT | 符号 | 含义 |
|---|---|---|
| | 找不到或无法加载合适的运行时版本。这是最常见的垫片错误 —— 在仅安装了v4的机器上,旧版激活被限制时调用方就会收到这个错误。 |
| | 找到了运行时,但无法从中获取所需的导出函数或接口。 |
| | 注册表中.NET Framework的安装根目录缺失或无效。 |
| | 缺少安装所需的组件。 |
| | 已有另一个运行时被绑定为旧版运行时,旧版API尝试绑定的版本与已选择的版本冲突。 |
| | 垫片正在关闭,无法处理请求。 |
如果用户反馈出现了这些HRESULT(尤其是),CLR激活日志是合适的诊断工具。
0x80131700Prerequisites
前置要求
CLR activation logging must be enabled to produce log files. If the user doesn't have logs yet, instruct them to enable logging:
Via environment variable (recommended — scoped to current session):
set COMPLUS_CLRLoadLogDir=C:\CLRLoadLogsVia registry (machine-wide — affects all .NET Framework processes):
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework
CLRLoadLogDir = "C:\CLRLoadLogs" (REG_SZ)On 64-bit systems, also set under if 32-bit processes are involved.
Wow6432Node⚠️ The log directory must already exist. The shim will not create it. If it doesn't exist, no logs will be written and there will be no error or indication of failure.
Logs are written as (NN = 00–99, one per process instance). Logs cannot be read until the process exits — the file is held open.
{ProcessName}.CLRLoad{NN}.logAfter capturing, remove the env var or registry key to stop logging.
必须先启用CLR激活日志记录才能生成日志文件。如果用户还没有日志,指导他们开启日志记录:
通过环境变量开启(推荐 —— 仅作用于当前会话):
set COMPLUS_CLRLoadLogDir=C:\\CLRLoadLogs通过注册表开启(机器级别 —— 影响所有.NET Framework进程):
HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\.NETFramework
CLRLoadLogDir = "C:\\CLRLoadLogs" (REG_SZ)在64位系统上,如果涉及32位进程,还需要在下设置相同的配置。
Wow6432Node⚠️ 日志目录必须已提前创建,垫片不会自动创建目录。如果目录不存在,不会生成任何日志,也不会有错误提示或失败通知。
日志文件以格式生成(NN = 00–99,每个进程实例对应一个日志文件)。进程退出前无法读取日志,文件会处于被占用状态。
{ProcessName}.CLRLoad{NN}.log捕获日志后,删除环境变量或注册表项以停止日志记录。
Inputs
输入项
| Input | Required | Description |
|---|---|---|
| CLR activation log files | Yes | One or more |
| Symptom description | Recommended | What the user observed (FOD dialog, wrong runtime, failure, etc.) |
| Expected behavior | Recommended | What the user expected to happen |
| 输入项 | 是否必填 | 描述 |
|---|---|---|
| CLR激活日志文件 | 是 | 一个或多个 |
| 症状描述 | 推荐 | 用户观察到的现象(FOD对话框、运行时错误、故障等) |
| 预期行为 | 推荐 | 用户期望的运行结果 |
Workflow
工作流程
Step 1: Load Reference Material
步骤1:加载参考材料
Try to load the reference files in this order — they contain the detailed log format, decision flow, and CLSID registry documentation:
- — Log line format, fields, and all known log message types
references/log-format.md - — The shim's decision tree for runtime selection
references/activation-flow.md - — COM (DllGetClassObject) activation specifics, CLSID registry layout
references/com-activation.md
If reference files are not available, proceed using the inline knowledge below.
按以下顺序加载参考文件,它们包含详细的日志格式、决策流程和CLSID注册表文档:
- —— 日志行格式、字段和所有已知的日志消息类型
references/log-format.md - —— 垫片选择运行时的决策树
references/activation-flow.md - —— COM(DllGetClassObject)激活细节、CLSID注册表结构
references/com-activation.md
如果没有参考文件,可使用下文的内嵌知识继续操作。
Step 2: Survey the Log Files
步骤2:概览日志文件
Get the big picture before diving into any single log:
- List all log files and group by process name — this shows which executables triggered CLR activation
- For each process, scan for outcome lines:
- — successful resolution
Decided on runtime: vX.Y.Z - — failed resolution
ERROR: - — FOD dialog was shown
Launching feature-on-demand - — FOD would have fired but was suppressed
Could have launched feature-on-demand - — v4+ was skipped due to capping
V2.0 Capping is preventing consideration
grep -l "ERROR:\|Launching feature-on-demand\|Could have launched" *.log
grep -c "Launching feature-on-demand" *.log- Build a summary table:
| Process | Log Files | Outcome | Runtime Selected | FOD? |
|---|---|---|---|---|
| ... | ... | ... | ... | ... |
在深入分析单个日志前先了解整体情况:
- 列出所有日志文件并按进程名分组 —— 这可以显示哪些可执行文件触发了CLR激活
- 针对每个进程,扫描结果行:
- —— 解析成功
Decided on runtime: vX.Y.Z - —— 解析失败
ERROR: - —— 已显示FOD对话框
Launching feature-on-demand - —— FOD被抑制,本应触发
Could have launched feature-on-demand - —— 由于版本限制,跳过了v4+版本
V2.0 Capping is preventing consideration
grep -l "ERROR:\\|Launching feature-on-demand\\|Could have launched" *.log
grep -c "Launching feature-on-demand" *.log- 构建汇总表:
| 进程 | 日志文件 | 结果 | 选中的运行时 | 是否触发FOD? |
|---|---|---|---|---|
| ... | ... | ... | ... | ... |
Step 3: Analyze Problematic Logs
步骤3:分析问题日志
For each log file with an unexpected outcome, trace the full activation flow. Read the log top-to-bottom and identify:
⚠️ Nested log entries: The shim's own internal calls can trigger additional log entries within an activation sequence that is already being logged. For example, acall may internally callDllGetClassObject, which callsComputeVersionString, each generating log lines. When the FOD check runs ("Checking if feature-on-demand installation would help"), it re-runs the entire version computation — producing a secondFindLatestVersionblock within the same activation. Don't mistake these nested/re-entrant entries for separate activation attempts.ComputeVersionString
针对每个结果不符合预期的日志文件,追踪完整的激活流程。从上到下阅读日志,识别以下内容:
⚠️ 嵌套日志条目: 垫片自身的内部调用可能会在正在记录的激活序列中触发额外的日志条目。例如,调用可能在内部调用DllGetClassObject,后者又调用ComputeVersionString,每个调用都会生成日志行。当运行FOD检查时(“Checking if feature-on-demand installation would help”),会重新运行完整的版本计算 —— 在同一个激活流程中生成第二个FindLatestVersion块。不要将这些嵌套/重入的条目误认为是独立的激活尝试。ComputeVersionString
3a. Entry Point
3a. 入口点
The first or line tells you how activation was triggered:
FunctionCall:MethodCall:| Entry Point | Meaning |
|---|---|
| Managed EXE launch — the binary IS a .NET assembly |
| COM activation — something CoCreated a COM class routed through mscoree.dll |
| Modern (v4+) hosting API |
| Legacy (v1/v2) hosting API — binds the process to one runtime |
| Policy-based hosting API (often called internally after other entry points) |
| Legacy API to load a framework DLL by name |
第一行或会告诉你激活是如何触发的:
FunctionCall:MethodCall:| 入口点 | 含义 |
|---|---|
| 托管EXE启动 —— 该二进制文件本身就是.NET程序集 |
| COM激活 —— 某个程序通过CoCreate创建了经由mscoree.dll路由的COM类 |
| 现代(v4+)托管API |
| 旧版(v1/v2)托管API —— 将进程绑定到单个运行时 |
| 基于策略的托管API(通常在其他入口点之后被内部调用) |
| 按名称加载框架DLL的旧版API |
3b. Input Parameters
3b. 输入参数
Immediately after the entry point, the log dumps the version computation inputs:
- : Is this a legacy (pre-v4) activation path? If 1, the shim uses the single-runtime "legacy" view of the world. Legacy APIs (
IsLegacyBind,CorBindToRuntimeExfor legacy COM,DllGetClassObject, etc.) set this.LoadLibraryShim - : If 1, the shim's roll-forward semantics are capped at Whidbey (v2.0.50727) — it will NOT consider v4.0+ when enumerating installed runtimes. This is the mechanism that makes v4 installation non-impactful: legacy codepaths continue to behave as if v4 doesn't exist. On a v4-only machine with no .NET 3.5, a capped enumeration sees no runtimes at all. Capping does NOT prevent loading v4+ if a specific v4 version string is explicitly provided (e.g., via
IsCappedor via config withCorBindToRuntimeEx("v4.0.30319", ...)).useLegacyV2RuntimeActivationPolicy - : Controls SKU (edition) compatibility checking.
SkuCheckFlags - : Whether to pretend this is an EXE launch for policy purposes.
ShouldEmulateExeLaunch - : Whether a legacy bind is strictly required.
LegacyBindRequired
在入口点之后,日志会立即输出版本计算的输入参数:
- : 是否为旧版(v4之前)激活路径?如果为1,垫片会使用单运行时的“旧版”逻辑。旧版API(
IsLegacyBind、旧版COM的CorBindToRuntimeEx、DllGetClassObject等)会设置这个参数。LoadLibraryShim - : 如果为1,垫片的前向滚动语义被限制在Whidbey(v2.0.50727)版本 —— 枚举已安装运行时时不会考虑v4.0+版本。这是让v4安装不影响旧系统的机制:旧代码路径会继续运行,就像v4不存在一样。在仅安装了v4、没有.NET 3.5的机器上,被限制的枚举会看不到任何运行时。如果明确提供了特定的v4版本字符串(例如通过
IsCapped或者配置了CorBindToRuntimeEx("v4.0.30319", ...)),版本限制不会阻止加载v4+。useLegacyV2RuntimeActivationPolicy - : 控制SKU(版本)兼容性检查。
SkuCheckFlags - : 是否出于策略目的模拟EXE启动。
ShouldEmulateExeLaunch - : 是否严格要求旧版绑定。
LegacyBindRequired
3c. Config File Processing
3c. 配置文件处理
Look for config file parsing results:
- — the shim is looking for a
Parsing config file: {path}file.config - — config file found and opened successfully
Config File (Open). Result:00000000 - — config file not found (HRESULT for ERROR_FILE_NOT_FOUND)
Config File (Open). Result:80070002 - — config was successfully read
Found config file: {path} - — whether
UseLegacyV2RuntimeActivationPolicy is set to {0|1}is present. When 1, all runtimes are treated as candidates for legacy codepaths — meaning legacy shim APIs can enumerate and choose v4+. This can be used with multiple<startup useLegacyV2RuntimeActivationPolicy="true">entries, with other config options, or even with no<supportedRuntime>entries at all (in which case legacy APIs can simply enumerate v4). Side effect: turns off in-proc SxS with pre-v4 runtimes — locks them out of the process.<supportedRuntime> - — each
Config file includes SupportedRuntime entry. Version: vX.Y.Z, SKU: {sku}found in config<supportedRuntime>
Key insight: If a process has no config file AND is doing a capped legacy bind, the shim has nothing to direct it to v4.0. It will enumerate installed runtimes (capped to ≤v2.0), find nothing if 3.5 isn't installed, and fail. This is by design — v4 is intentionally invisible to these codepaths to keep v4 installation non-impactful.
查找配置文件解析结果:
- —— 垫片正在查找
Parsing config file: {path}文件.config - —— 找到并成功打开配置文件
Config File (Open). Result:00000000 - —— 未找到配置文件(ERROR_FILE_NOT_FOUND的HRESULT)
Config File (Open). Result:80070002 - —— 成功读取配置文件
Found config file: {path} - —— 是否存在
UseLegacyV2RuntimeActivationPolicy is set to {0|1}配置。当值为1时,所有运行时都会被视为旧代码路径的候选 —— 意味着旧版垫片API可以枚举并选择v4+版本。可以配合多个<startup useLegacyV2RuntimeActivationPolicy="true">条目、其他配置选项使用,甚至可以不搭配<supportedRuntime>条目(这种情况下旧版API可以直接枚举v4)。副作用: 关闭进程内v4之前版本运行时的SxS支持 —— 禁止这些版本加载到进程中。<supportedRuntime> - —— 在配置中找到的每个
Config file includes SupportedRuntime entry. Version: vX.Y.Z, SKU: {sku}条目<supportedRuntime>
关键要点: 如果进程没有配置文件,并且正在执行受限制的旧版绑定,垫片没有任何引导指向v4.0。它会枚举已安装的运行时(限制为≤v2.0),如果未安装3.5则找不到任何运行时,最终失败。这是设计使然 —— v4对这些代码路径故意不可见,以保证v4安装不会影响旧系统。
3d. Version Resolution
3d. 版本解析
- — what's installed on the machine
Installed Runtime: vX.Y.Z. VERSION_ARCHITECTURE: N - — version from the binary's PE header (managed assemblies only; native EXEs won't have this)
{exe} was built with version: vX.Y.Z - — the shim picked a version from the config's
Using supportedRuntime: vX.Y.Zlist<supportedRuntime> - — result of policy-based latest-version search
FindLatestVersion is returning the following version: vX.Y.Z ... V2.0 Capped: {0|1} - or
Default version of the runtime on the machine: vX.Y.Z— what the shim settled on;(null)means nothing was found(null) - — final decision — this is the version that will be loaded
Decided on runtime: vX.Y.Z
- —— 机器上安装的运行时
Installed Runtime: vX.Y.Z. VERSION_ARCHITECTURE: N - —— 二进制文件PE头中的版本(仅托管程序集有,原生EXE没有)
{exe} was built with version: vX.Y.Z - —— 垫片从配置的
Using supportedRuntime: vX.Y.Z列表中选择了一个版本<supportedRuntime> - —— 基于策略的最新版本搜索结果
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
3e. Failure and FOD Path
3e. 失败和FOD路径
If version resolution fails:
- — the shim found no suitable runtime
ERROR: Unable to find a version of the runtime to use - — checks the process error mode:
SEM_FAILCRITICALERRORS is set to {value}- Value 0: Error dialogs and FOD are ALLOWED
- Nonzero (any bit set, commonly 0x8001): Error dialogs and FOD are SUPPRESSED. The flag (0x0001) is inherited from the parent process.
SEM_FAILCRITICALERRORS
- — the shim re-runs version computation to see if installing .NET 3.5 would resolve the request
Checking if feature-on-demand installation would help - Then either:
- — FOD dialog shown
Launching feature-on-demand installation. CmdLine: "...\fondue.exe" /enable-feature:NetFx3 - — FOD suppressed because
Could have launched feature-on-demand installation if was not opted out.was setSEM_FAILCRITICALERRORS
如果版本解析失败:
- —— 垫片没有找到合适的运行时
ERROR: Unable to find a version of the runtime to use - —— 检查进程错误模式:
SEM_FAILCRITICALERRORS is set to {value}- 值为0: 允许弹出错误对话框和FOD
- 非0(任意位被设置,通常为0x8001): 禁止弹出错误对话框和FOD。标志(0x0001)会从父进程继承。
SEM_FAILCRITICALERRORS
- —— 垫片重新运行版本计算,判断安装.NET 3.5是否能解决请求
Checking if feature-on-demand installation would help - 之后会出现两种情况之一:
- —— 已显示FOD对话框
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
3f. Multiple Activations in One Process
3f. 单个进程中的多次激活
A single log can contain multiple activation sequences. Each begins with a new or entry. A common pattern:
FunctionCall:MethodCall:- First activation via /
ClrCreateInstance→ succeeds (loads v4.0 via config)GetRequestedRuntime - Second activation via (COM) → legacy bind, capped → fails
DllGetClassObject
This happens when a native EXE (like link.exe or mt.exe) loads the CLR successfully for its primary work, then a secondary COM activation request (e.g., for diasymreader) triggers a separate legacy resolution that can't find v2.0.
单个日志可能包含多个激活序列,每个序列都以新的或条目开头。常见模式:
FunctionCall:MethodCall:- 第一次通过/
ClrCreateInstance激活 → 成功(通过配置加载v4.0)GetRequestedRuntime - 第二次通过(COM)激活 → 旧版绑定,受版本限制 → 失败
DllGetClassObject
当原生EXE(例如link.exe或mt.exe)为了主要工作成功加载CLR后,后续的COM激活请求(例如diasymreader的请求)会触发独立的旧版解析,无法找到v2.0时就会出现这种情况。
Step 4: Check System State (if needed)
步骤4:检查系统状态(如有需要)
When log analysis points to a registration or configuration issue, check:
CLSID Registration (for COM activation issues):
powershell
undefined当日志分析指向注册或配置问题时,检查以下内容:
CLSID注册(针对COM激活问题):
powershell
undefinedCheck the CLSID entry
检查CLSID条目
Get-ItemProperty 'Registry::HKCR\CLSID{guid}'
Get-ItemProperty 'Registry::HKCR\CLSID{guid}\InprocServer32'
Get-ChildItem 'Registry::HKCR\CLSID{guid}\InprocServer32' | ForEach-Object {
Write-Output "--- $($.PSChildName) ---"
Get-ItemProperty "Registry::$($.Name)"
}
Key values under `InprocServer32`:
- `(Default)` should be `mscoree.dll` for CLR-hosted COM objects
- **Version subkeys** (e.g., `2.0.50727`, `4.0.30319`) indicate which runtime versions registered this CLSID
- **`ImplementedInThisVersion`** under a version subkey means that runtime version natively implements the COM class (not via managed interop)
- **`Assembly`** and **`Class`** under a version subkey indicate a managed COM interop registration
- **`RuntimeVersion`** under a version subkey specifies which CLR version should host this object
**Installed runtimes:**
```powershell
Get-ChildItem 'Registry::HKLM\SOFTWARE\Microsoft\.NETFramework\policy'Process error mode (why FOD did/didn't fire):
The flag is inherited from the parent process. If a build system or script sets it (or calls ), all child processes inherit it.
SEM_FAILCRITICALERRORSSetErrorModeGet-ItemProperty 'Registry::HKCR\CLSID\{guid}'
Get-ItemProperty 'Registry::HKCR\CLSID\{guid}\InprocServer32'
Get-ChildItem 'Registry::HKCR\CLSID\{guid}\InprocServer32' | ForEach-Object {
Write-Output "--- $($.PSChildName) ---"
Get-ItemProperty "Registry::$($.Name)"
}
`InprocServer32`下的关键值:
- `(默认)`对于CLR托管的COM对象应该为`mscoree.dll`
- **版本子键**(例如`2.0.50727`、`4.0.30319`)表示哪个运行时版本注册了这个CLSID
- 版本子键下的**`ImplementedInThisVersion`**表示该运行时版本原生实现了这个COM类(不是通过托管互操作)
- 版本子键下的**`Assembly`**和**`Class`**表示托管COM互操作注册
- 版本子键下的**`RuntimeVersion`**指定应该托管该对象的CLR版本
**已安装的运行时:**
```powershell
Get-ChildItem 'Registry::HKLM\\SOFTWARE\\Microsoft\\.NETFramework\\policy'进程错误模式(FOD是否触发的原因):
标志从父进程继承。如果构建系统或脚本设置了该标志(或调用了),所有子进程都会继承这个设置。
SEM_FAILCRITICALERRORSSetErrorModeStep 5: Diagnose and Report
步骤5:诊断并报告
Produce a clear diagnosis covering:
- What happened — which process(es) had activation issues and what the symptom was
- Why it happened — trace through the specific decision path in the shim that led to the outcome
- What controls the behavior — identify the specific inputs (config file presence, error mode, CLSID registration, capping state) that determined the outcome
- What changed (if applicable) — if the user says behavior changed, identify which input could have changed (error mode from parent process, config file, CLSID registration, installed runtimes)
生成清晰的诊断结果,包含以下内容:
- 发生了什么 —— 哪些进程出现了激活问题,症状是什么
- 为什么会发生 —— 追溯垫片中导致该结果的具体决策路径
- 行为的控制因素 —— 确定决定结果的具体输入(配置文件是否存在、错误模式、CLSID注册、版本限制状态)
- 发生了什么变化(如适用)—— 如果用户反馈行为发生了变化,找出可能变化的输入(父进程的错误模式、配置文件、CLSID注册、已安装的运行时)
Common Scenarios
常见场景
Unexpected FOD Dialogs
意外的FOD对话框
Pattern: → → no config file → → → FOD launched
DllGetClassObjectIsCapped: 1(null)SEM_FAILCRITICALERRORS: 0Root cause: A native EXE is doing COM activation of a CLSID registered under mscoree.dll. This takes the legacy codepath, which is capped at v2.0. With no config file (and no ), v4 is invisible to this codepath. On a machine without .NET 3.5, there are no runtimes visible, and with not set, the FOD dialog fires.
useLegacyV2RuntimeActivationPolicySEM_FAILCRITICALERRORSKey question: Why did change? It's inherited from the parent. Different launch methods (script vs. direct invocation, different build systems) produce different error modes. The underlying capped-legacy-bind-on-v4-only-machine failure is always there — it's just that controls whether it manifests as a visible dialog or a silent failure.
SEM_FAILCRITICALERRORSSEM_FAILCRITICALERRORS模式: → → 无配置文件 → → → 触发FOD
DllGetClassObjectIsCapped: 1(null)SEM_FAILCRITICALERRORS: 0根本原因: 原生EXE正在对注册在mscoree.dll下的CLSID执行COM激活,走的是旧版代码路径,被限制为仅查找v2.0版本。没有配置文件(也没有)的情况下,v4对该代码路径不可见。在未安装.NET 3.5的机器上,找不到任何运行时,且未设置,因此触发FOD对话框。
useLegacyV2RuntimeActivationPolicySEM_FAILCRITICALERRORS关键问题: 为什么发生了变化?它是从父进程继承的。不同的启动方式(脚本 vs 直接调用、不同的构建系统)会产生不同的错误模式。底层的“仅v4机器上受限制的旧版绑定失败”问题一直存在 —— 只是控制了它是表现为可见对话框还是静默失败。
SEM_FAILCRITICALERRORSSEM_FAILCRITICALERRORSWrong Runtime Selected
选择了错误的运行时
Pattern: entries in config list multiple versions; the shim picks the first one that's installed. If v2.0 is listed first and .NET 3.5 is installed, v2.0 wins even though v4.0 is also available.
supportedRuntimeKey insight: Config entries are evaluated in order. First installed match wins.
<supportedRuntime>模式: 配置中的条目列出了多个版本;垫片会选择第一个已安装的版本。如果v2.0排在第一位且已安装.NET 3.5,即使v4.0也可用,也会选择v2.0。
supportedRuntime关键要点: 配置中的条目按顺序评估,第一个匹配的已安装版本会被选中。
<supportedRuntime>Both v2 and v4 Loaded
同时加载v2和v4
Pattern: Multiple activation sequences in the same process log — one binds v4, another binds v2 (or vice versa). Side-by-side loading of CLR v2 and v4 in the same process IS supported but can cause issues with shared state.
Key insight: Look for separate lines with different versions in the same log file.
Decided on runtime模式: 同一个进程日志中有多个激活序列 —— 一个绑定了v4,另一个绑定了v2(反之亦然)。同一进程中并行加载CLR v2和v4是支持的,但可能会导致共享状态相关的问题。
关键要点: 在同一个日志文件中查找不同版本的行。
Decided on runtimeLegacy Runtime Already Bound
旧版运行时已绑定
Pattern: A legacy codepath succeeds early in the process (e.g., with an explicit v4 version, or config with ). This sets the legacy runtime to v4.0. All subsequent legacy activations — including capped COM activations that would otherwise fail — silently succeed by reusing the already-bound legacy runtime.
CorBindToRuntimeExuseLegacyV2RuntimeActivationPolicyKey insight: The ORDER of activations within a process matters. If v4.0 is bound as the legacy runtime first, capped COM activations work. If the capped COM activation happens first (before any legacy runtime is bound), it fails. This means behavior can depend on which component activates first — a race condition in concurrent code can change the outcome.
模式: 进程早期旧版代码路径执行成功(例如带明确v4版本的调用,或者配置了),这会将旧版运行时设置为v4.0。所有后续的旧版激活 —— 包括原本会失败的受限制COM激活 —— 都会复用已绑定的旧版运行时,静默成功。
CorBindToRuntimeExuseLegacyV2RuntimeActivationPolicy关键要点: 进程内激活的顺序很重要。如果首先将v4.0绑定为旧版运行时,受限制的COM激活就能正常工作。如果受限制的COM激活先发生(在绑定任何旧版运行时之前),就会失败。这意味着行为取决于哪个组件先激活 —— 并发代码中的竞态条件可能会改变结果。
Common Pitfalls
常见误区
| Pitfall | Correct Approach |
|---|---|
Assuming | Capping only restricts roll-forward enumeration. v4.0 can still be loaded if: a specific version string is passed explicitly, config has |
| Thinking capping is broken or a bug | Capping is intentional — it makes v4 installation non-impactful. On a v4-only machine, legacy codepaths correctly see no runtimes. This is working as designed. |
| Assuming FOD is controlled per-process | |
| Looking only at the first activation in a log | A single log can contain multiple independent activation sequences. The problematic one is often a secondary COM activation, not the initial CLR load. |
| Assuming a missing config file is benign | For native EXEs doing COM activation with legacy/capped bind, the config file (with |
Adding | Without |
Setting | This attribute turns off in-proc SxS — it locks pre-v4 runtimes out of the process. This is usually fine for build tools but should be considered for apps that need to host both v2 and v4. |
| 误区 | 正确做法 |
|---|---|
认为 | 版本限制仅限制前向滚动枚举。以下情况仍可加载v4.0:明确传入特定版本字符串、配置中设置了 |
| 认为版本限制是故障或Bug | 版本限制是故意设计的 —— 它让v4安装不会影响旧系统。在仅安装v4的机器上,旧代码路径看不到任何运行时是符合预期的行为。 |
| 认为FOD是按进程控制的 | |
| 仅查看日志中的第一个激活 | 单个日志可能包含多个独立的激活序列,有问题的通常是后续的COM激活,而非初始的CLR加载。 |
| 认为缺少配置文件没有影响 | 对于执行旧版/受限制绑定COM激活的原生EXE,配置文件(带 |
仅添加 | 没有 |
设置 | 这个属性会关闭进程内SxS —— 禁止v4之前的运行时加载到进程中。这对于构建工具通常没问题,但对于需要同时托管v2和v4的应用需要谨慎考虑。 |
Validation
验证
Before delivering a diagnosis, verify:
- All log files with errors or FOD triggers were analyzed (not just the first one)
- The entry point for each problematic activation was identified
- The capping and legacy bind state was noted for each activation sequence
- Config file presence/absence was checked
- SEM_FAILCRITICALERRORS state was noted for FOD-related issues
- Multiple activations within a single log were individually traced
- The diagnosis explains the specific decision path, not just the outcome
在输出诊断结果前,确认以下内容:
- 所有包含错误或FOD触发的日志文件都已分析(不只是第一个)
- 每个有问题的激活的入口点都已识别
- 每个激活序列的版本限制和旧版绑定状态都已记录
- 已检查配置文件是否存在
- FOD相关问题已记录状态
SEM_FAILCRITICALERRORS - 单个日志中的多次激活都已单独追踪
- 诊断解释了具体的决策路径,而不只是结果",