ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Windows Server 2012离线安装.NET 3.5报错排查指南

Windows Server 2012离线安装.NET 3.5报错排查指南

简介:针对Windows Server 2012 R2及SQL Server部署中频繁出现的.NET Framework 3.5安装失败——提示找不到源文件或需要指定源位置——这套亲测有效的修复资源提供了终极解决思路。包内共1568个文件,涵盖720个dll动态库、180个resx资源文件、84个exe可执行程序及56个sql脚本等,同时包含config、targets、reg等配置与注册表文件,能够满足离线安装、组件修复和系统配置等多种场景。压缩包整体约99.19MB,结构完整,适合云服务器管理员、运维工程师及数据库部署人员使用。已有10328人学习下载,实测可绕过“指定源位置”报错,帮助快速完成.NET 3.5功能启用,避免在无数无效方案中反复试错。

1. 装个 .NET 3.5 卡到怀疑人生:Windows Server 2012 的经典翻车现场

在一台全新的 Windows Server 2012 上装 SQL Server,前置检查第一项就是 .NET Framework 3.5。服务器没有外网,双击安装包半天没反应,添加角色和功能向导转了一圈然后弹 0x800F0906;去网上搜了一堆所谓离线包,下一个放进去还是同样的错,最后只能对着屏幕发懵。这个场景在 Server 2012 / 2012 R2 上实在太过常见,SQL Server 2012、2014 的完整安装向导把 .NET 3.5 当作硬性前置项,缺了它整个安装流程都走不下去,而系统默认又不会给你直接装上这个老框架。

问题的根子在于:Windows Server 2012 默认不启用 .NET 3.5,在线安装只有一条路就是走 Windows Update,而离线安装要求你必须有一个和当前系统版本严格匹配的源文件(sxs 目录)。不是随便找个 .NET 3.5 安装包就能糊弄过去的,版本对不上、源目录权限不对、Windows Update 服务被策略锁住,任何一个环节出问题都表现为"无法安装 .NET 3.5",而且报错还长得完全不一样。这篇笔记就是写给卡在这一步的人,从版本识别、离线源准备到三条安装路线和五个常见报错的救法,照着跑完,SQL Server 前置检查就能稳稳变绿。

2. 动手前先分清版本和源:Server 2012 与 2012 R2 的坑不一样

2.1 先确认系统版本,避免拿错离线源

很多人栽在第一步:根本不清楚自己那台机器到底是不是 2012 R2,就随便在网上找了个 sxs 压缩包。Windows Server 2012 的正式版本号是 6.2,2012 R2 是 6.3,两个系统的 .NET 3.5 离线源是不通用的,把 2012 的源装到 2012 R2 上,报错就是 0x800F081F。先花三十秒把版本确认下来,后面能少走两个小时的弯路。

# 用 systeminfo 看操作系统版本那一行 systeminfo | findstr /B /C:"OS 名称" /C:"OS 版本" # 用 PowerShell 直接读系统信息 Get-CimInstance Win32_OperatingSystem | Select-Object Caption, Version, BuildNumber

systeminfo的输出里,OS 版本如果是 6.2.9200 就是 Windows Server 2012,6.3.9600 就是 2012 R2。这个数字直接决定你要找哪个离线 sxs 源。另一个容易忽略的点是系统架构,x64 和 x86 的源包也不能混用,服务器绝大多数是 x64,但个别老机器会有 x86 的情况,确认一下没坏处。

拿到结论之后,我一般会把系统版本号用便签贴在服务器机箱上,或者在远程桌面的壁纸上写上版本号。听起来很玄学,但在维护几十台服务器的环境里,分辨哪台是什么版本靠肉眼根本不行,每次下载离线源之前先核对一遍版本,这是我后来再没在源文件上翻过车的习惯。

2.2 离线源文件:sxs 目录该长什么样

把这台服务器要用的离线安装资源准备好,压缩包解压之后你会看到sources\sxs这个典型的目录结构。很多人不知道 sxs 目录是什么,它其实就是 Windows 的"按需功能"组件存储位置,装 .NET 3.5 时系统不是去执行一个 exe 安装包,而是从这个目录里的NetFx3.cab提取组件文件。理解这一点很重要,因为你在网上找到的大多数所谓 ".NET 3.5 安装包"实际上是给客户端系统用的运行库,放到服务器上用 DISM 装根本不认。

正确做法是:解压对应的 sxs 目录,然后把它放到一个稳定路径下,比如C:\sxs,不要在服务器上挂载 ISO 直接引用光盘路径。原因有两个:一是服务器可能装完系统之后光驱会被占用来做别的,或者物理机重启后光盘不见了,再运行安装就会出现找不到源的诡异情况;二是共享盘或外接存储可能因为驱动问题间歇性掉线。我踩过一次性价比很高的坑:明明刚才还看得到光盘里的 sxs,装到一半提示找不到源文件,一查是光盘休眠了。

2.3 系统里的三个前置状态检查

打开服务器管理器之前,先看三个状态。第一是 Windows Update 服务,很多人会用组策略把 Windows Update 停掉,这在断网环境很常见,但 .NET 3.5 的功能添加核心也会去访问 WU,服务停了在线方式必挂。第二是看系统有没有已挂起的重启,如果系统在等你重启安装更新,任何功能添加操作都可能报 0x800F0922。第三是看 .NET Framework 4.x 是否可写,如果 4.x 的某次更新把目录权限改掉了,3.5 装的进度也会受影响,虽然不太常见但碰到了很头疼。

用管理员的 PowerShell 窗口执行下面这个检查,一次把三个状态全看掉:

# 检查 Windows Update 服务状态 Get-Service -Name wuauserv | Select-Object Name, Status # 检查系统是否待重启,注册表里 PendingReboot 如果为 1 就先把重启做了 Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing" -Name PendingReboot -ErrorAction SilentlyContinue # 检查 .NET 4.5 的版本号,Server 2012 自带 4.5 Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" | Select-Object Release

wuauserv的 Status 如果不是 Running,要么启动服务,要么就老老实实走离线安装的 DISM 路线,不要白费力气去图形界面点。PendingReboot这个键如果存在而且值是 1,先重启服务器再继续装,否则后面所有功能添加都可能报系统忙。Release 值只要存在就行,它主要告诉你 4.x 框架在不在,3.5 的安装过程需要和 4.x 共存,不要指望把 4.x 卸了装 3.5,这俩不是替代关系。

3. 正式安装:图形向导、DISM、PowerShell 三条路线怎么选

3.1 图形界面路线:服务器管理器添加功能

如果服务器有图形界面,大多数人的第一反应是从服务器管理器里点。操作路径是:打开服务器管理器 → 添加角色和功能 → 功能选项卡 → 勾选 .NET Framework 3.5 Features → 指定备用源路径,填C:\sxs,然后让它跑。这个流程本身不复杂,但这里有一个特别折磨人的点:在安装过程中,向导界面会出现一个"指定备用源路径"的输入框,它默认让你填的是安装介质根目录,不是 sxs 目录本身。

常见错误是往这个框里填C:\sxs\sources\sxs,或者填C:\sxs之后系统仍提示找不到源。正确做法要分清楚:如果你指定的是包含sources\sxs的介质根,填根路径;如果已经把 sxs 单独拷出来了,填 sxs 所在的那一层路径,也就是包含NetFx3.cab的那一层。我自己习惯直接把 sxs 文件夹里的内容放到C:\sxs下,也就是让C:\sxs\NetFx3.cab存在,这样填写C:\sxs肯定能识别。

# 如果图形界面走不动,先用 PowerShell 把功能启用起来 Install-WindowsFeature -Name Net-Framework-Core -Source "C:\sxs"

Install-WindowsFeature是服务器管理器底层使用的 cmdlet,-Name Net-Framework-Core是 .NET 3.5 功能在系统里的完整代号,-Source指向刚才准备的 sxs 路径。这个方法的好处是比图形界面更直观,成功与否直接反映在命令返回值里,不会出现向导界面转了十分钟然后一句话不说消失的情况。

3.2 DISM 路线:离线源安装最稳的一条

如果服务器是 Server Core 环境,或者图形界面因为各种原因完全没法用,DISM 是唯一的路径,也是我个人最推荐的方式。它不像服务器管理器那样依赖 WU 服务,只要源路径里文件完整,几乎不会出现幺蛾子。

dism /online /enable-feature /featurename:NetFx3 /all /source:C:\sxs /limitaccess

这个命令的参数值得逐一说清楚。/online表示操作当前运行中的系统,不要动离线映像;/featurename:NetFx3是目标功能名,注意大小写无所谓但这个拼写必须一字不差;/all表示启用该功能的所有父功能,.NET 3.5 有一堆附属组件,这一步必须带;/source:C:\sxs指定功能源的搜索路径;/limitaccess则是让 DISM 只从指定路径和系统自带 Windows 目录里找源文件,不要连 Windows Update。这个参数是断网服务器的救命稻草,少了它系统会去尝试 WU 然后等半天超时失败。

命令跑完会有进度条,走到 100% 之后提示"操作成功完成"。出现报错也不要关窗口,DISM 会把具体的错误码打出来,比如Error: 0x800F0906,这个错误码在下一章我们逐条拆。一个容易被忽视的点是:DISM 命令本身要以管理员权限运行,普通管理员组用户的 cmd 窗口如果 UAC 没提权,命令会直接拒绝执行。

3.3 PowerShell 按需功能方式:另一种等价写法

除了 DISM,还有一条 PowerShell 的原生语法也可以启用 .NET 3.5,它调用的底层机制和 DISM 是一致的,但更细化的参数直观一些。注意,这里和刚才的Install-WindowsFeature是同一类命令,不过下面这个写法带-LimitAccess,行为更加接近 DISM 的"死磕本地源"逻辑。

# 启用功能并限制只能从本地源获取 Enable-WindowsOptionalFeature -Online -FeatureName NetFx3 -All -Source "C:\sxs" -LimitAccess

Enable-WindowsOptionalFeature是 Windows 8 / Server 2012 时代新增的 cmdlet,和 DISM 参数一一对应,-Online对应 /online,-FeatureName NetFx3对应功能名,-Source指定源路径。区别主要在返回形式上,PowerShell 会返回一个RestartNeeded字段,告诉你是否需要重启。我一般用这个命令来判断装的进度,如果看到RestartNeeded : False心里就踏实了。

用它还有一个小好处:如果你不确定系统当前 .NET 3.5 的启用状态,可以先执行Get-WindowsOptionalFeature -Online -FeatureName NetFx3,看到State : Disabled,再执行启用,更规范。DISM 没有这种查询带看的方式,要靠另一个命令查看,是的,这是个小差异。

3.4 装完后别急着重启:先看状态再动手

无论用哪条路线,装完之后系统会提示你要不要重启。经验是:如果提示 RestartNeeded 是 False,就不用重启,直接确认结果;如果是 True,别犹豫,重启。但重启之前先做一次确认,用下面命令看安装状态:

Get-WindowsOptionalFeature -Online -FeatureName NetFx3 | Select-Object State Get-WindowsFeature Net-Framework-Core | Select-Object Name, InstallState

如果返回State : Enabled或者InstallState : Installed,说明核心已就位。这时候再去控制面板"启用或关闭 Windows 功能"里看,.NET Framework 3.5 Features 前面的复选框应该是实心钩状态。这里插一个容易自欺欺人的现象:有些人装到一半看到对话框显示成功就直接去跑 SQL Server 安装程序,结果前置检查还是红,回头再看,原来功能启用了但没启用完全。原因后头细说,现在只要记住:装完组件之后,去 IWR 里把所有子项都展开看一眼,HTTP Activation 和非 HTTP 激活这些附加项该勾就勾。

4. 常见安装失败排查:五个我踩过且能救回来的坑

4.1 0x800F0906:报错千千万,这个最常见

现象是运行 DISM 或者服务器管理器添加功能后报错Error: 0x800F0906,后面往往还跟一句The source files could not be downloaded。这个报错出现在没有外网的服务器上简直是标配:它表示系统想要从 Windows Update 拉取源文件失败。原因有两种,一是服务器根本连不上更新服务,二是本地组策略把 WU 访问给锁了。

解决的第一步,是给命令加上/limitaccess(DISM)或者-LimitAccess(PowerShell),强制系统不要去找 Windows Update。第二步检查本地策略:gpedit.msc→ 计算机配置 → 管理模板 → 系统 → 指定 Windows 更新设置,如果"配置自动更新"被禁用,连带着按需功能也会受影响。把这个策略改成"未配置"或者直接从注册表删掉对应项,再重新运行安装命令就能过。如果策略确实不能被改动(有些生产环境被域策略锁死),那就彻底放弃向导,用 DISM 加 LimitAccess,源路径指对就能绕过 WU 依赖。

4.2 0x800F081F:源文件存在却找不到,十有八九版本对不上

现象比较诡异:明明指定了C:\sxs,里面NetFx3.cab就在那,DISM 却报Error: 0x800F081F,提示找不到源文件。很多人会陷入死循环,反复确认路径,甚至把权限改成 Everyone 可读也没用。原因我后来才明白:这个报错不只是看路径,还要校验源文件的版本与当前系统是否匹配。Windows Server 2012 的 sxs 装到 2012 R2 上就是这样,文件看得见,系统就是不肯用。

解决的办法很笨但有效:去拿到和当前系统 BuildNumber 完全一致的安装介质或 sxs 源。怎么看有没有拿对?回到第二章的Win32_OperatingSystem查询,记住 BuildNumber,再去源的sources\sxs里找一个带有版本号的文件,和系统版本核对。顺手还要看一个东西:如果系统装过某些重要更新,比如 2012 R2 打了一些补丁之后,旧的 sxs 源可能也会失效,需要到补丁对应的更新包里去提源,不过这种场景在纯离线环境的比例很小,核对 BuildNumber 基本够用。

4.3 0x800F0907:策略强制从 Windows 更新拉取源

现象是 DISM 执行到一半,报Error: 0x800F0907,提示因为组策略设置无法从 Windows 更新下载源。这个和 0x800F0906 的区别在于:后者是系统连不上 WU 而失败,前者是系统明明能连 WU,但被策略告知不许从 WU 拉取,或者反过来策略要求必须从 WU 拉取而本地离线源被忽略。出现这个错,说明这台服务器的组策略里面有关于"从 Windows 更新下载功能"的强制规定。

解决办法是查注册表:HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate下面如果存在DisableWindowsUpdateAccess或者其他策略残留,会影响功能源选择。我一般直接用 DISM 指定源路径的同时加/NoRestart,不是这个报错的解药;正解是临时删策略键,安装完成后再把策略恢复回去。改注册表之前记得先备份键值,生产服务器上乱动组策略是有风险的事,能不动就不动,能动就要留后悔药。

4.4 0x800F0922:系统重启挂起和镜像占用并发

现象是 DISM 报Error: 0x800F0922,这个错误码很杂,多数和重启状态或系统服务状态有关。有一次我怎么都装不上,一直报这个错,后来发现是服务器挂载了 DVD 镜像而且光驱正在被占用来做系统备份,同时系统里有一个待重启的文件重命名操作卡着系统组件服务。

解决思路分两步:先重启一次把系统状态清干净,再确认光驱或者镜像被释放。如果重启之后还报,看 CBS 日志C:\Windows\Logs\CBS\CBS.log,里面会打出更细的信息,比如某个组件存储的锁被占用。排查锁占用一般我会先停掉 Windows Search 服务和 BITS 服务再重试,这两个服务在功能安装期间确实可能干扰组件存储。

4.5 安装明明成功,SQL Server 前置检查还是红的

最让人血压升高的一种情况是这个:DISM 显示操作成功,PowerShell 里查状态也是 Enabled,但打开 SQL Server 安装中心跑到"安装规则"那一步,.NET Framework 3.5 这一项依然是红色的失败。原因是 SQL Server 检测的不仅仅是 .NET 3.5 运行库本体,它会看 .NET 3.5 SP1 的具体版本号。Server 2012 的 sxs 源不同批次提供的 .NET 3.5 SP1 版本会有差别,如果系统里装载的是某个月度更新后的版本,而 SQL Server 要求的是更具体的版本区间,就会出现这种"功能在,满足不了"的错位。

解决方法是去注册表确认版本:HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5下的Version值,正常应该是3.5.30729.xxx,如果看到的是停在 3.5.30729.01 没打 SP1 补丁,就缺 KB 补丁。Server 2012 上需要确认的是系统有没有装全该装的更新,如果服务器完全离网又没法补这个版本,那就只能换一个更新版本的 sxs 源重新装,没有别的捷径。这个场景里 SQL Server 安装日志也会给出具体提示,把sqlservr.err打开看,里面会写清楚到底缺什么组件。

5. 装完怎么验证跑通:别急着开 SQL Server 安装包

我吃过一次亏:DISM 装完 .NET 3.5,看着屏幕上的"操作成功完成"就以为万事大吉,直接去跑 SQL Server 安装程序,结果前置检查第四项还是红的,整个人当场傻眼。所以从那以后我每次装完都强制自己走一遍完整的验证流程,几分钟的事,能省掉后面半小时的返工。验证其实就四步,按顺序执行。

# 第一步:确认功能状态 Get-WindowsOptionalFeature -Online -FeatureName NetFx3 | Select-Object State # 第二步:确认注册表版本 Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5" | Select-Object Version, SP # 第三步:确认程序集文件真的落盘了 Test-Path "C:\Windows\Microsoft.NET\Framework64\v3.5" Test-Path "C:\Windows\Microsoft.NET\Framework\v3.5"

第一步如果State不是Enabled,说明刚才的操作根本没生效,回头要看 CBS 日志找原因。第二步的Version读到3.5.30729.01且SP为 1,说明 .NET 3.5 SP1 就位,如果 SP 是 0 或者键不存在,SQL Server 那一关还是过不去。第三步是查看两个目录是否存在,Framework64那个是 64 位程序在用的,Framework是 32 位兼容层,两个目录同时存在才算完整。SQL Server 安装向导会同时检查这几个维度。

第四步是实际跑一段目标代码验证运行库可用,别只看文件在不在,文件在不一定能运行起来。写一个三行的小程序,用 .NET 3.5 编译并执行一遍,能跑就说明运行环境真的通了。

# 第四步:用 csc 编译一个最简单的控制台程序进行运行时验证 Set-Content -Path C:\test35.cs -Value 'class T{ static void Main(){ System.Console.WriteLine("ok"); } }' C:\Windows\Microsoft.NET\Framework\v3.5\csc.exe /nologo /out:C:\test35.exe C:\test35.cs C:\test35.exe

这段代码做的事非常朴素:用 .NET 3.5 自带的 C# 编译器csc.exe把一个只输出 ok 的程序编译成 exe,然后执行它。如果第二行能找到csc.exe,说明编译器和工具链都已经装好;执行后看到 ok,说明运行时也正常。这是一个不管 SQL Server 还是任何依赖 .NET 的地方都能用得上的终极确认手段。

四步全绿之后,再打开 SQL Server 安装中心,前置检查里 .NET 3.5 那一项会直接变成绿色对勾,往下走就不会再被卡住了。这套流程看起来很基础,但它就是能拦截掉我前面写的四个翻车场景,比任何嘴硬都有说服力。从那以后我每装一台新服务器,无论部署的是什么应用,都会强制走一遍这套验证再继续,虽然多花五分钟,但换来的是后续装数据库、装中间件时彻底不用在这个老框架上再做第二次功课。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进