简介:面向 Windows Server 2012 系统管理员与运维人员,这份 SxS 镜像资源包用于在无完整安装介质或网络受限的环境下补齐 .NET Framework 3.5 所需组件,缓解通过 Windows Update 安装时常见的下载失败、速度缓慢与服务启用受阻等问题,也可作为旧版应用兼容性部署的本地组件来源。压缩包共 1569 个文件、约 101.69MB,以 dll、resx、exe、aspx、config、sql、browser、ini、tlb 等为主,覆盖框架运行库、资源文件、配置与安装支撑内容,并附随包使用说明,便于按目录结构定位所需 SxS 组件。已有 10501 人学习下载。资源提供离线安装所需的本地源,适合理解 NetFx3 功能启用与组件依赖关系,也可用于排查安装失败时对照文件完整性和路径设置,提升在受限网络与断网服务器上的部署效率。
1. 那行报错不是系统坏了:0x800F0906 卡住的到底是什么
一台跑着老业务系统的 Windows Server 2012,双击安装包,弹出「需要安装 .NET Framework 3.5」;点「安装此功能」,等一会儿,跳出 0x800F0906。换成服务器管理器手动勾选 .NET Framework 3.5 功能,还是同一句话:无法安装以下功能。这不是系统坏了。从这一代 Windows 开始,.NET 3.5 的安装文件不再随系统盘一起落地,系统里只剩一个「功能名」,真正装的时候才去找源文件。联网机器还能从更新里拉,内网离线机器就只能靠一份和系统版本对齐的 sxs 目录把 .net 3.5 安装这件事手动补完。下面按我实际踩过的顺序讲:三个错误码各自指向哪里,--sxs(准确写法是/Source:...\sources\sxs)那行命令怎么拼,装完怎么验,以及装上了却还是起不来的时候该往哪看。
2. 三个错误码不是一回事:0x800F0906、0x800F081F、0x800F0907 的根因拆解
2.1 报错现场长什么样,触发条件各不相同
同样是「无法完成更改」,三条路径完全不同。0x800F0906 的完整提示里通常带一句「Windows 无法从 Windows 更新下载所需文件」,这句话已经把根因写在脸上了:系统试图联网取载荷,但取不到。可能是机器根本不通外网,可能是 DNS 解析不了更新地址,也可能是上游更新服务被内网策略接管之后,那台服务器上压根没有 .NET 3.5 的包。这条错误跟你的命令写法无关,跟你有没有指定源有关。
0x800F081F 的提示是「找不到源文件」,意思是:你确实指定了源,但系统在那个位置没找到它能用的东西。这一条最容易误判成「路径写错了」,实际上路径写对却依然报 081F 的情况非常常见,原因集中在三处:指向的目录不对(指到了 ISO 根目录或sources而不是sources\sxs)、目录里的载荷文件和当前系统版本或语言对不上、文件在拷贝过程中被截断。
0x800F0907 最少见也最容易查错方向。它一般出现在被域策略管着的服务器上,提示大意是「组策略不允许从 Windows 更新下载」,或者干脆没有下载动作就直接失败。根因不在系统本身,而在「指定 Windows 功能安装和修复的设置」这条策略被指向了内部更新服务器,并且勾了「从不安装来自 Windows 更新的功能」。这种情况下你把镜像挂得再对,DISM 也不会去读它,因为它压根没打算找源。
2.2 错误码对照:先对号,再动手
| 错误码 | 提示关键词 | 根因落点 | 第一动作 |
|---|---|---|---|
| 0x800F0906 | 无法从 Windows 更新下载 | 没有可用源,联网路径不通 | 挂载同版本 ISO,走/Source |
| 0x800F081F | 找不到源文件 | 源路径不对、版本/语言不匹配、载荷缺失 | 先确认sources\sxs下的 cab 存在 |
| 0x800F0907 | 组策略阻止 | 修复源被策略指向内部服务器 | 临时还原策略,再配合/LimitAccess |
| 0x800F0954 | 无法联系更新服务 | 客户端侧更新源不可达 | 同上,改走本地源 |
| 0x80073712 | 组件存储损坏 | 系统组件仓库本身有问题 | 先/Cleanup-Image /RestoreHealth |
这张表我一般贴在运维记录第一页。因为绝大多数人第一次遇到时只会看到一行「安装失败」,然后开始搜「.net3.5安装失败」,搜到的方案五花八门,有让改注册表的,有让重装系统的,实际上只要先分清错误码属于哪一类,后面能省掉一大半时间。
2.3 先判断一件事:是没源,还是有源被拦
在敲安装命令之前,花三十秒确认策略状态,比装到一半失败再回滚划算得多。组策略相关的注册表位置固定,用 reg query 看一眼就行:
:: 看更新源是否被策略接管,返回 0x1 说明走了内部更新服务器 reg query "HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" /v UseWUServer :: 看功能修复源是否被强制指定,存在这个键就说明策略在起作用 reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing" /v RepairContentServerSource :: 顺带确认系统版本和内部版本号,后面选镜像要用 systeminfo | findstr /B /C:"OS Name" /C:"OS Version"第一条返回0x1,说明这台机器的更新行为已经被内部服务器接管,此时系统的默认逻辑是「去内部服务器找」,而内部服务器大概率没同步 .NET 3.5 的可选组件。第二条如果查到了值,说明功能修复源被策略钉死,DISM 会优先按那个地址找,本地镜像反而被忽略。
处理办法是临时把策略还原成「未配置」:在本地策略编辑器里进「计算机配置 - 管理模板 - 系统」,找到「指定 Windows 功能安装和修复的设置」,设为未配置,然后执行gpupdate /force让策略立刻生效。装完再改回去。这一步在域环境里要跟管策略的人打个招呼,别自己改完就跑,否则下一次策略刷新又会被覆盖回去,然后你会以为是「装好了又掉了」,白折腾一轮。
顺带说一句,/LimitAccess这个参数的作用就是让 DISM 不要去联系更新服务。很多人以为加上它就万事大吉,实际上它只挡住了「联网下载」这个动作,挡不住「策略强制指定了另一个源」。所以正确的顺序是:先看策略,再用/LimitAccess把联网这条路彻底堵死,让系统老老实实读你给的目录。
3. 挂载镜像补源:/Source:...\sources\sxs那行命令的完整写法
3.1 挂镜像、认盘符、确认载荷真的在
网上把这一步叫--sxs,其实 DISM 没有这个参数,真实写法是/Source:<路径>\sources\sxs,--sxs只是大家口头的简称。既然是简称,就有人真的照着敲--sxs,然后得到一句「无效的参数」,这也是这类问题里最常见的乌龙之一。
挂载镜像我习惯用 PowerShell,因为盘符能自动取出来,不用盯着「我的电脑」数:
# 只读挂载 ISO,-PassThru 返回对象,后面才能取盘符 $iso = Mount-DiskImage -ImagePath 'E:\iso\SERVER2012_R2_X64.iso' -PassThru # 从挂载结果里取盘符,避免手写盘符写错 $dl = ($iso | Get-Volume).DriveLetter Write-Host "源目录: $dl`:\sources\sxs" # 关键一步:确认载荷文件真的在这个目录里 Get-ChildItem "$dl`:\sources\sxs" -Filter 'microsoft-windows-netfx3-ondemand-package*' | Select-Object Name, Length-PassThru不加的话,Mount-DiskImage什么都不返回,$iso是空的,后面取盘符直接报错。Get-Volume拿到的DriveLetter是个字符,拼路径时要补冒号。最后那句过滤是关键:正常情况下你应该看到一个microsoft-windows-netfx3-ondemand-package.cab,大小在两百多兆这个量级。看不到它,就别往下走了,先解决源的问题——目录里没这个文件,DISM 一定会给你 0x800F081F,换什么参数都没用。
如果Get-ChildItem输出为空,而目录本身存在,常见原因是这份 ISO 是精简过的安装介质,或者你挂的是某个第三方打包的「合辑镜像」。这类镜像的sources\sxs经常被裁掉。判断方法是看sources目录下有没有install.wim或者install.esd,有安装映像但没有 sxs 载荷,就是被裁过。这种情况只能换回官方原版同版本镜像。
3.2 正式安装:一行命令,四个部分
:: 以管理员身份运行 cmd :: /Online 表示对当前运行的系统操作,不是离线映像 :: /FeatureName:NetFx3 是 .NET 3.5 的功能名,写错会报「功能名称未知」 :: /All 连同父级功能一起启用 :: /Source 指向 sxs 目录,注意是目录不是 cab 文件 :: /LimitAccess 阻止 DISM 回连更新服务 dism /Online /Enable-Feature /FeatureName:NetFx3 /All /Source:D:\sources\sxs /LimitAccess :: 立刻看返回码,0 才是成功,3010 表示需要重启 echo %errorlevel%命令执行过程中会显示进度条,走到 100% 之后才是判定时刻。很多人看到进度条满格就以为成了,实际上后面的「操作成功完成」才是结论。返回码 0 表示装好了,3010 表示装好了但要重启才生效,其他任何数字都是失败,此时往上翻两屏,DISM 会把具体原因打出来,那行才是你要拿去搜的东西,而不是笼统的「安装失败」。
/FeatureName的写法必须精确:NetFx3。写成NetFramework3、NetFx35、NETFX3都会失败,DISM 的功能名是大小写不敏感但拼写敏感。查当前系统里到底叫什么名字,用dism /Online /Get-Features | findstr /I "netfx",会列出所有带 netfx 的功能名,一眼就能对上。
3.3/All、/LimitAccess、/Source三个参数各自管什么
/Source管「从哪读」,可以给多个路径,用分号隔开,DISM 会依次尝试。实际用的时候给一个就够了,给多了反而容易因为某个路径不可访问而拖慢速度。路径里带空格必须加引号,这在「D 盘新建文件夹」这种随手起的名字上翻车率极高。
/All管「父功能要不要一起开」。.NET 3.5 在功能树里不是叶子节点,它下面挂着 WCF 激活、HTTP 激活这些子项。不加/All,主功能能装上,但依赖它的子功能仍然是关的,表现出来就是「装是装上了,程序还是起不来」。这一条在后面第 5 章还会再提一次,因为它是「安装成功但无效」的头号原因。
/LimitAccess管「要不要联网」。不加它,DISM 在本地源里找不到东西时会自己转头去问更新服务,于是你在内网环境里会看到命令卡住几十秒然后报 0x800F0906——明明给了源,却报了个「下载失败」,非常迷惑。加上它,DISM 就只认你给的路径,找不到立刻失败,错误信息也更干净。离线环境我建议无脑加。
3.4 装完立刻验证:三条命令,缺一不可
:: 1) 看功能状态,State 应该是 Enabled dism /Online /Get-FeatureInfo /FeatureName:NetFx3 :: 2) 看注册表标记,Install 应该是 0x1 reg query "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5" /v Install :: 3) 看运行库目录是否落地,正常会有 v3.5.30729 这个版本目录 dir C:\Windows\Microsoft.NET\Framework\v3.5.30729第一条看功能状态,State : Enabled是终点。如果显示Enable Pending,说明还在等重启,重启后再查。第二条看注册表,Install的值是0x1才算真正注册进系统,有些情况下功能状态是 Enabled 但注册表没写,程序照样找不到运行库。第三条看物理目录,v3.5.30729是 .NET 3.5 的版本号,目录存在说明文件确实落地了。
这三条我一般连着跑,因为它们的失败方式不一样:功能状态对但目录不存在,多半是安装过程中断;目录存在但注册表没写,多半是被某些「优化工具」清过;三条全对但程序还报错,那就是程序侧的问题,不是运行库的问题——这时候要去查 IIS 或者应用配置,而不是继续折腾安装。
4. 换一种写法:PowerShell 路径与免挂载的落地做法
4.1 ServerManager 模块:Install-WindowsFeature的等价写法
命令行不止 DISM 一种。Server 2012 上装了 ServerManager 模块,可以直接用 PowerShell 写,好处是能拿到结构化返回值,方便塞进脚本里做批量。
# 引入服务器管理模块,没有这步会提示命令不存在 Import-Module ServerManager # Net-Framework-Core 就是 .NET 3.5 在功能树里的名字,和 DISM 的 NetFx3 是同一个东西 Install-WindowsFeature -Name Net-Framework-Core ` -Source 'D:\sources\sxs' ` -IncludeAllSubFeature ` -Restart:$false-Source的值和 DISM 一样指向 sxs 目录,不是 cab 文件。-IncludeAllSubFeature等价于 DISM 的/All,把子功能一起带上。-Restart:$false表示先不自动重启,方便你先把其他步骤跑完再统一重启,不然脚本跑到一半机器重启了,后面全断。
这条命令返回一个对象,Success为True才算成功,RestartNeeded告诉你是否需要重启。批量场景下我一般这么写:
$r = Install-WindowsFeature -Name Net-Framework-Core -Source 'D:\sources\sxs' -IncludeAllSubFeature if (-not $r.Success) { Write-Host "失败,退出码: $($r.ExitCode)" -ForegroundColor Red } elseif ($r.RestartNeeded -eq 'Yes') { Write-Host "安装完成,需要重启" -ForegroundColor Yellow } else { Write-Host "安装完成,无需重启" -ForegroundColor Green }ExitCode的取值和 DISM 返回码是同一套语义,0成功、3010需重启。有这个东西,几十台机器跑一遍就能筛出哪几台失败,不用一台台点。
如果系统是 2012 R2 并且 PowerShell 版本在 4.0 以上,还可以用 DISM 模块的Enable-WindowsOptionalFeature,写法更接近客户端系统:
Enable-WindowsOptionalFeature -Online -FeatureName NetFx3 ` -Source 'D:\sources\sxs' ` -All -LimitAccess -NoRestart参数含义和 DISM 一一对应:-All对应/All,-LimitAccess对应/LimitAccess,-NoRestart对应不自动重启。区别在于这条命令在老一些的 2012 上可能提示模块不可用,遇到就直接退回 DISM,别在这上面耗时间。
4.2 没有图形界面:把 sxs 目录先拷到本地盘
远程到一台机器、又没有挂载 ISO 的权限时,我一般把整个 sxs 目录先复制到系统盘,再指向本地路径。这样做的好处是断开远程会话之后安装不会中断,坏处是占两百多兆空间,装完记得清。
# /E 含子目录,/NFL /NDL 不打印文件列表,避免刷屏 robocopy D:\sources\sxs C:\sxs /E /NFL /NDL # 复制完核对文件数,和源目录对比一下 (Get-ChildItem C:\sxs -Recurse -File).Count (Get-ChildItem D:\sources\sxs -Recurse -File).Count两个数字必须一致。不一致就是复制过程被中断了,最常见的原因是目标盘空间不够,robocopy 会在日志末尾提示。这种半截的目录拿去当源,DISM 报的就是 0x800F081F,然后你会以为是镜像的问题,重新下载一遍镜像,浪费一晚上。
复制完之后,把/Source:D:\sources\sxs换成/Source:C:\sxs就行,其余参数一律不动。装完用Remove-Item C:\sxs -Recurse -Force清掉。
4.3 装完之后的「服务开启」:IIS 与 ASP.NET 2.0 注册
.net 3.5服务开启这个说法在实际场景里通常指两件事:一是运行库本身装上了,二是依赖它的服务能起来。前者靠上面几节解决,后者经常被漏掉。
如果这台机器跑 IIS,装完 .NET 3.5 之后还要把 ASP.NET 3.5 的处理程序注册进去,否则站点会给你 500.19 或者「无法识别的配置节」。老教程会让你跑aspnet_regiis -i,这个做法在 2012 上已经不是推荐路径了,正确的方式是启用对应功能:
# Web-Asp-Net 对应 IIS 里的 ASP.NET 3.5 支持 Install-WindowsFeature -Name Web-Asp-Net # 如果应用用了 WCF,还需要 HTTP 激活 Install-WindowsFeature -Name WCF-HTTP-ActivationWeb-Asp-Net装完,IIS 管理器里的处理程序映射才会出现v2.0.50727那一组。装之前站点报 500.19,装之后正常,这个因果关系我在好几台机器上验证过。WCF-HTTP-Activation则是给走 HTTP 协议的服务用的,缺了它,服务起来但调用方一直超时,日志里只有一句含糊的「没有终结点在侦听」,排查起来非常费劲。
装完这些,重启一次 IIS(iisreset),再用浏览器访问站点确认。别只看「功能已启用」就收工,服务能不能起来是另一回事。
5. 避坑记录:五条踩过的坑,每条都能多耗两小时
5.1 路径指向了 ISO 根目录,而不是sources\sxs
现象:命令里写着/Source:D:\或/Source:D:\sources,回车之后十几秒报 0x800F081F,提示找不到源文件。你去看路径,目录明明存在。
原因:DISM 不会递归去猜载荷放在哪一层,它只认你给的那个目录,且要求目录里直接躺着microsoft-windows-netfx3-ondemand-package.cab。指到上一层,它翻不到就报错。
解决:把路径精确到sources\sxs。不确定的时候先dir D:\sources\sxs,能看到 cab 文件再执行安装。这一步花五秒,能省掉一轮无意义的排查。
5.2 镜像版本或语言和系统对不上
现象:路径完全正确,cab 文件也在,仍然 0x800F081F,或者装到一半回滚。
原因:拿 2012 R2 的 sxs 去喂 2012,或者拿中文版镜像给英文版系统补,都会失败。载荷文件是按版本和语言签名的,系统在安装时会校验,对不上就拒绝。这个错误提示极其含糊,只说「找不到源文件」,完全不提版本不符。
解决:用systeminfo里的OS Version和系统语言去挑镜像,版本号、语言、架构三项全对上。宁可多下一个镜像,也别指望跨版本凑合。
5.3 组件存储本身坏了,装什么都失败
现象:换了三个镜像、试了两种命令,全部失败,错误码有时是 0x800F081F,有时是 0x80073712。
原因:系统的组件仓库(WinSxS)有损坏,DISM 在写入新组件时校验不过。这种情况下源再完美也没用,因为问题不在源这一侧。
解决:先修仓库再装组件。
:: 先扫描,看有没有可修复的损坏 dism /Online /Cleanup-Image /ScanHealth :: 有损坏就修,这一步需要源,同样指向 sxs dism /Online /Cleanup-Image /RestoreHealth /Source:D:\sources\sxs /LimitAccess :: 仓库修完再装 .NET 3.5 dism /Online /Enable-Feature /FeatureName:NetFx3 /All /Source:D:\sources\sxs /LimitAccess顺序不能反。先装组件再修仓库,等于在坏地基上盖房子,修完仓库组件还是缺的。/RestoreHealth跑完会给出修复结果,成功之后再执行安装,成功率明显不一样。
5.4 远程会话里挂载之后盘符不见了
现象:Mount-DiskImage提示成功,但资源管理器里没有新盘符,命令里写的D:指向的是本来的数据盘。
原因:盘符冲突。服务器上本来就有 D 盘,ISO 挂载时系统会自动分配下一个可用字母,往往不是你以为的那个。远程桌面会话里有时盘符重定向也会干扰,导致你看到的和执行时用的不是同一个。
解决:别猜盘符,用Get-Volume取。挂载后立刻执行($iso | Get-Volume).DriveLetter拿到真实字母,再拼进命令。或者用-DriveLetter参数在挂载时直接指定一个确定没被占用的字母,比如Z。
5.5 装上了,但老程序还是说缺 .NET 3.5
现象:三条验证命令全过,功能是 Enabled,注册表是0x1,目录也在,但程序启动还是弹「需要 .NET Framework 3.5」。
原因:八成是漏了子功能,或者漏了应用侧依赖。/All没加的情况最常见——主功能装上了,WCF 那一层没装,程序调用 WCF 的接口就报错,而错误文案恰好和「缺 .NET 3.5」长得很像。另一种是 IIS 站点没注册 ASP.NET 3.5,表现出来也是类似的错。
解决:补装子功能,再查注册。
:: 查看当前 .NET 3.5 下所有子功能状态 dism /Online /Get-Features | findstr /I "netfx"输出里凡是NetFx3相关的项,状态都应该是 Enabled。有 Disabled 的,用/All重新跑一遍安装命令补上。IIS 场景再补Web-Asp-Net,然后iisreset。这三步做完还有问题,就该去查应用程序自己的配置文件了,别继续在运行库上绕。
6. 快速定位:三分钟分清是源、策略还是系统本身
我现在接到「.NET 3.5 装不上」这类活,不急着敲安装命令,先花三分钟做判断。顺序固定:先看错误码,再看策略,最后验源。这个顺序是有讲究的——错误码决定方向,策略决定路径通不通,源决定最后能不能成。反过来做,会在「换个镜像再试试」这种动作上反复循环。
判断表如下,照着对号就行:
| 你看到的 | 大概率是 | 下一步 |
|---|---|---|
| 提示里带「从 Windows 更新下载」 | 根本没给源 | 挂同版本 ISO,加/Source和/LimitAccess |
| 提示「找不到源文件」,路径肉眼可见存在 | 路径层级或版本语言不对 | dir到 sxs 层,核对 OS 版本与语言 |
| 提示被组策略阻止,或无下载动作直接失败 | 修复源被策略接管 | 还原策略 +gpupdate /force |
| 报 0x80073712,或换源多次仍失败 | 组件存储损坏 | 先/RestoreHealth再装 |
| 安装成功但程序仍报错 | 子功能缺失或 IIS 未注册 | 补/All、补Web-Asp-Net |
具体到操作,我一般按这五步走,每步都有明确的判定点,不做没有输出的动作:
systeminfo确认版本号、语言、架构,据此选镜像。判定点:三项和手上的 ISO 完全一致。reg query查两条策略,有值就先还原。判定点:两条都返回「找不到指定的注册表项」。- 挂载 ISO,
Get-ChildItem确认 cab 存在且大小正常。判定点:文件名和体积都对。 - 执行 DISM 安装,看返回码。判定点:
0或3010。 - 三条验证命令连跑。判定点:状态、注册表、目录三样全对。
这五步里任何一步判定不过,就停在那一步解决,不要往下走。因为后面的步骤依赖前面的结果,源不对的时候硬装,只会得到一个错误码,然后你再拿这个错误码去搜,搜到的方案又是从第一步开始的,绕一圈回到原点。
还有一个细节值得说:失败之后别急着重启。很多人一看失败就重启,觉得「重启能解决百分之九十的问题」。在 DISM 这个场景里,重启会让挂载的 ISO 掉盘,/Source路径失效,下一次执行直接报路径不存在,于是错误码变了,你以为遇到了新问题,其实是自己制造的。先把错误码和命令行完整记下来,再决定要不要重启。
从那以后我每次碰这类离线组件安装,都强制走一遍「版本—策略—源—装—验」这五步,哪怕只有一台机器。血泪经验是省下来的那两分钟,最后都变成了两小时。这份资源里放的就是和系统版本对齐的 sxs 源目录,以及上面这几组我实际跑通的命令,遇到装不上的时候,先按第 6 章的顺序对一遍,多数情况不用重装系统就能过去。希望帮到你。
本文还有配套的精品资源,点击获取