☰
Server 2012 离线安装 .NET 3.5 与 NetFx3 排错
2026/10/24 4:16:52 网站建设 项目流程

简介:面向 Windows Server 2012 R2 等云服务器环境的运维人员与系统管理员,这份压缩包针对安装 .NET Framework 3.5 时提示“找不到源文件”或要求使用“源”选项指定还原位置的常见故障,整理了可离线指定源的安装文件集合,属于偏实操排错向的系统部署资料。包内共 1568 个文件,约 99.19MB,以 720 个 dll、84 个 exe、60 个 config、56 个 sql、66 个 aspx 与 180 个 resx 等为主,覆盖运行库组件、配置项、数据库脚本与 Web 管理页面等模块,目录层次较完整,便于按模块检索所需文件。已有 10328 人学习下载,说明该问题在服务器部署场景中较为普遍。读者可借助其中的源文件结构,对照系统组件缺失时的排错思路,减少反复寻找安装介质的时间,也可用于同类环境的功能还原与文件核对。

1. 一台内网 Server 2012,卡在 SQL Server 安装程序的第一个红叉

上个月在一台 Windows Server 2012 上部署 SQL Server,安装程序跑到「安装程序支持规则」这一步,直接弹出一个红叉:需要 .NET Framework 3.5 SP1。这台机器是纯内网,没有外网出口,我顺手敲了一句dism /online /enable-feature /featurename:NetFx3,等了两分钟,返回 0x800F0906——意思是「源文件下载不到」。在 Windows Server 2012 上,.NET 3.5 属于按需功能,默认不带在系统里,运行时再去网上拉,内网环境几乎都会失败。这个问题让人觉得有点难以解释:系统里明明写着 .NET 4.5,为什么 SQL Server 还非要 3.5?更麻烦的是装到一半失败,系统里留下一堆待重启标记,再装第二次就会报出其他错误。这篇记录就是我当时那台机器从卡住到装通的全过程,如果你也在给 2012 或 2012 R2 补这个组件,照着走即可。

2. 装前把环境列清:NetFx3、源目录、权限三件事

动手之前先把三件事确认掉,能省掉后面一大半反复尝试的时间。一是功能名到底叫什么,二是源从哪来,三是当前这台机器的状态允不允许你装。很多人失败不是命令写错,而是拿了一个「看起来对」的包,或者在一个已经被组策略管住的机器上硬试。

2.1 要装的是 NetFx3,不是再装一遍 4.x

.NET Framework 的 2.0/3.5 和 4.x 是两套并行的运行时,CLR 版本分别是 2.0 和 4.0,装在同一个系统里互不影响,也不互相替代。Windows Server 2012 出厂自带 4.5,所以你在「程序和功能」里能看到 .NET Framework 4.5,但NetFx3这个功能仍然是「已禁用」状态。SQL Server 2012 的安装引导程序、部分共享组件,以及一些老的 ASP.NET 2.0/3.5 站点,依赖的正是 CLR 2.0 那一套,你装十个 4.8 都填不上这个坑。

判断方法很简单,看注册表里有没有 v3.5 这个键:

reg query "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5" /v Install

返回0x1说明已就位;返回「系统找不到指定的注册表项」就是没装。这一条比看控制面板靠谱,因为控制面板在 Server 2012 上只列 4.x。

另外提醒一句:别去下那种几十兆的在线安装器(dotNetFx35setup.exe)。它在有外网的桌面系统上确实能跑通,但在服务器上,它最终还是回头去调 DISM 从 Windows 更新拉取组件,绕一圈还是 0x800F0906,白折腾。

2.2 三条安装通道,选哪条

同一个功能,落地方式有好几种,差别全在「源」上。我把常见的几条列出来对比:

通道命令或做法适用场景主要失败点
在线从 Windows 更新拉dism /online /enable-feature /featurename:NetFx3机器能直连外网0x800F0906、0x800F0954
离线 sxs 源目录dism ... /Source:D:\sxs /LimitAccess内网机器、批量部署源目录不完整、盘符写错
挂载 install.wim 取 WinSxSdism /mount-wim后指向挂载目录手头只有系统 ISO索引选错、版本不匹配
组策略指定备用源GPO + 本地路径域内几十台机器统一策略没刷新、路径无权限

生产环境我基本只用第二条:把离线源固定放在一个不随人变动的路径上(比如D:\sxs),后面所有命令、脚本、组策略都指向它。第三条是应急用的,只在实在找不到干净源的时候才挂 ISO,注意必须用同版本、同架构的镜像,2012 的源给 2012 R2 用,大概率会在后半段报错。

2.3 装前十分钟的检查清单

真正开始敲命令之前,这几项我每次都会过一遍:

# 1. 确认系统版本与内部版本号,确认是 2012 还是 2012 R2 systeminfo | findstr /B /C:"OS Name" /C:"OS Version" # 2. 确认没有挂起的重启,有就先重启再装 reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending" # 3. 确认当前会话是提升过的管理员 whoami /groups | findstr /C:"S-1-16-12288" # 4. 确认目标盘剩余空间,sxs 源加上组件本身留 2GB 比较稳 Get-PSDrive D | Select-Object Name,Free

第一条看版本,是因为 2012 和 2012 R2 的补丁基线不同,源文件不能混用。第二条最容易忽略:只要 CBS 里有挂起标记,dism /enable-feature会直接拒绝执行,报一个和源完全无关的错,让人误以为是包的问题。第三条判断是不是真的提权了——Server Core 上用runas开的窗口有时并不是完整令牌。第四条是习惯,组件装完加补丁缓存,几十兆的源能膨胀到几百兆。

提示:检查清单里的命令建议在同一个 PowerShell 窗口里跑完,中途换会话(尤其是从远程桌面切到带/smartcard的登录方式)会导致环境变量和临时目录变化,日志也不好对齐。

3. 离线源落地:目录怎么摆,DISM 参数怎么填

这一步是把 zip 里的东西变成系统认得的「源」。核心只有一个要求:/Source指向的那个目录下,必须能直接看到microsoft-windows-netfx3-ondemand-package.cab这类负载文件,而不是它的上一层或下一层。

3.1 源目录的摆放方式和校验

我一般把解压出来的内容整理成这样,路径短、无空格、全英文,避免后面命令引号写错:

D:\sxs\ microsoft-windows-netfx3-ondemand-package.cab microsoft-windows-netfx3-ondemand-package.mum ...

如果拿到的是系统 ISO,源在\sources\sxs\下;如果是那种打包好的离线包,解压后一般也会保留sources\sxs的层级,把最里面那一层的内容整体搬到D:\sxs即可。判断标准就一条:进到目录里dir *.cab能看到文件。看不到,说明你搬的是父目录。

搬完做个快速校验,确认文件不是零字节、也没被压缩工具截断:

Get-ChildItem D:\sxs -Filter *.cab | Select-Object Name,Length,LastWriteTime

Length是 0 或者只有几 KB,基本可以判定包在传输过程中坏了,重新解压。这个坑我踩过:用某压缩软件「解压到当前文件夹」时中途取消,留下一个同名空文件,DISM 一路跑到 60% 才报错,日志里只给一句「源文件无效」,查了半天。

3.2 DISM 命令逐参数拆开看

:: 离线安装 NetFx3,源指向本地 sxs 目录,禁止回连 Windows 更新 dism /online /enable-feature /featurename:NetFx3 /all /Source:D:\sxs /LimitAccess :: 装完立刻查状态,别等重启后再确认 dism /online /get-featureinfo /featurename:NetFx3

逐段解释一下,这几个参数每一个都有人写错:

/online表示操作当前运行的系统,不是离线镜像。如果你是在 WinPE 里给离线系统打,要换成/image:C:\,这是两个完全不同的用法。

/enable-feature是启用功能,别写成/enable-featurename或者/install,DISM 对拼写很敏感,写错直接给你「错误 87 命令行参数不正确」。

/featurename:NetFx3是 DISM 世界的名字。PowerShell 那边叫Net-Framework-Core,别混用,DISM 不认后者,会报「功能名称未知」。

/all会把父功能一并启用。NetFx3 下面挂着 WCF、LINQ 这些子项,不加/all有时候只装了核心,后面 IIS 里跑站点还是缺东西。

/Source:D:\sxs指向包含 cab 的目录本身。注意:后面不要加空格,路径有空格要整体加双引号。

/LimitAccess是这条命令里最关键的开关,作用是禁止 DISM 在本地源失败时回连 Windows 更新或 WSUS。不加它,源有问题时它还是会去网上转一圈,最后给你一个更含糊的错误码,反而更难排查。

执行完没重启之前,功能状态可能显示「启用挂起」。这是正常的,重启一次再看。我见过有人看到「挂起」以为失败了,又跑一遍命令,结果把状态搞成两个 pending 操作叠加,最后只能等系统自己合并。

3.3 PowerShell 版本与无人值守脚本

图形界面点烦了之后,我改成用 PowerShell 写;它在 Server Core 上也能跑,还方便加日志:

# 启用 .NET 3.5,源指向本地 sxs,不重启,完成后写日志 Import-Module ServerManager $log = "C:\netfx3-install.log" Start-Transcript -Path $log -Append $r = Install-WindowsFeature -Name Net-Framework-Core -Source D:\sxs -Restart:$false $r | Format-List FeatureResult,Success,RestartNeeded # 顺带把 ASP.NET 3.5 和 WCF HTTP 激活一起装上,省一次重启 Install-WindowsFeature -Name Web-Asp-Net,WCF-HTTP-Activation -Source D:\sxs Stop-Transcript

-Name Net-Framework-Core对应 DISM 的 NetFx3,这是 PowerShell 的功能命名,别写错。-Source D:\sxs的语义和 DISM 的/Source一致,但 PowerShell 里不带LimitAccess这个开关,等价效果要靠组策略或注册表兜底(下一章会讲),否则源失效时它仍可能回连更新服务器。-Restart:$false是我故意加的,把重启时机交给运维窗口决定,而不是命令一跑完机器就重启。Format-List那段是把返回对象打出来,Success为True才算真的成功,光看命令有没有报红字不够。

脚本里我习惯把Web-Asp-Net和WCF-HTTP-Activation一起装掉。原因很实际:这两个也依赖 NetFx3 的源文件,等 IIS 站点报 500.19 再回头补,又要找一次源、又多一次重启。

注意:如果机器上已经有 .NET 4.x 的站点在跑,先装 3.5 再注册 ASP.NET 的顺序比较稳。反过来操作,IIS 的 ISAPI 筛选器顺序可能被改乱,站点会出现「已停止」但日志里什么都不写的情况。

4. 组策略与注册表:让系统自己去正确的源找文件

命令能一次跑通当然最好,但批量环境里我更倾向把「源在哪」这件事写进策略。这样后面任何人执行启用功能、或者系统自己触发组件修复,都不会跑到网上去。

4.1 那个「从不从 Windows 更新下载」的开关

路径在:计算机配置 → 管理模板 → 系统 → 指定可选组件安装和组件修复的设置。启用它,然后在「备用源文件路径」里填D:\sxs,同时勾上「从不尝试从 Windows 更新下载负载」。

这个策略解决的是 0x800F0954。很多域环境里,客户端被 WSUS 接管,DISM 想拉组件时会被重定向到内网 WSUS,而 WSUS 那边根本没同步按需功能负载,于是卡住。你在本机敲多少遍带/Source的命令都没用,因为策略层的重定向优先级更高。改完策略记得刷新:

gpupdate /force

改完不生效是常态,gpupdate只是把策略拉到本地,服务端的组件安装服务还要重启一次才认新配置:

Restart-Service wuauserv -Force

4.2 注册表兜底:LocalSourcePath 和 RepairContentServerSource

没有域、或者只是临时救一台机器,我直接写注册表,省得进 gpedit 点半天:

:: 指定组件修复时的本地源路径 reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing" ^ /v LocalSourcePath /t REG_EXPAND_SZ /d "D:\sxs" /f :: 2 = 从不尝试从 Windows 更新下载负载;1 = 直接联系 Windows 更新;3 = 从 WSUS 获取 reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing" ^ /v RepairContentServerSource /t REG_DWORD /d 2 /f :: 重启组件安装相关服务,让新策略生效 net stop wuauserv & net start wuauserv

LocalSourcePath用REG_EXPAND_SZ而不是REG_SZ,是因为路径里可能带环境变量,用可扩展类型将来换盘符时好改。RepairContentServerSource的取值要和实际策略对齐:设成 2 之后系统就彻底不碰 Windows 更新,全靠本地源;如果你的源并不完整,设 2 会把原本能在线补上的路也堵死,这点要提前想清楚。

还有一处容易被忽略:如果这台机器之前配过UseWUServer,即使你写了上面两个键,DISM 仍可能走 WSUS。临时排查时我会把它关掉再试:

reg add "HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" /v UseWUServer /t REG_DWORD /d 0 /f

排查完记得恢复,别把生产机的补丁策略一直挂着关。

4.3 IIS 侧配套:Web-Asp-Net 和 WCF 激活

.NET 3.5 装完只是有了运行时,IIS 要用它还得单独启用功能。这一步经常被跳过,然后站点报 500.19 或者「无法识别的配置节」。

# ASP.NET 3.5(Web-Asp-Net)与 4.5(Web-Asp-Net45)是两个独立功能 Install-WindowsFeature -Name Web-Asp-Net -Source D:\sxs # WCF 的 HTTP/TCP 激活,走 WAS 的站点才需要 Install-WindowsFeature -Name WCF-HTTP-Activation,WCF-TCP-Activation -Source D:\sxs # 查一下最终状态 Get-WindowsFeature Web-Asp-Net,WCF-HTTP-Activation | Select-Object Name,InstallState

Web-Asp-Net是 ASP.NET 3.5,Web-Asp-Net45是 4.5,名字只差一个数字,装错了站点照样起不来。InstallState要看到Installed。

这里还有一个流传很广的错误做法:用aspnet_regiis.exe -i注册。在 Windows Server 2012 上这条路基本走不通,工具要么提示当前系统不支持,要么执行完静默不生效,因为它从 Windows 8 / 2012 开始就被功能启用机制取代了。老老实实用Install-WindowsFeature。

5. 排查:五个真实出现过的报错

错误码是黑匣子,光看数字没用,得知道它对应哪一环。下面五条是我在实际机器上遇到并处理过的,按「现象 → 原因 → 解决」记。

5.1 源与路径类:0x800F0906、0x800F081F、错误 87

现象一:dism /online /enable-feature /featurename:NetFx3跑几十秒后返回 0x800F0906,提示「无法下载源文件」。原因:命令没带/Source,系统按默认策略去 Windows 更新拉负载,内网无出口。解决:补上/Source:D:\sxs /LimitAccess。加上/LimitAccess后错误码会变得具体,源不对就是 0x800F081F,比 0906 好定位得多。

现象二:带了/Source仍报 0x800F081F,提示「找不到源文件」。原因:三种可能——盘符变了(U 盘拔了、盘符被占)、/Source指向了sxs的上一层、或者源里的 cab 不完整。还有一种隐蔽情况:源是从别的版本系统上拷的,文件在但版本号对不上。解决:先dir D:\sxs\*.cab确认文件在,再确认路径层级。实在找不到干净源,挂同版本 ISO 取:

dism /Mount-Wim /WimFile:E:\sources\install.wim /Index:2 /MountDir:C:\mount dism /online /enable-feature /featurename:NetFx3 /all /Source:C:\mount\Windows\WinSxS /LimitAccess dism /Unmount-Wim /MountDir:C:\mount /Discard

/Index:2要按实际镜像里的版本索引改,先dism /Get-WimInfo /WimFile:E:\sources\install.wim看一眼,选 ServerStandard 或 DataCenter 对应那一项。

现象三:命令直接返回「错误 87 命令行参数不正确」。原因:新手最常犯的两个——/Source:D:\sxs里:后面多了空格,或者把 PowerShell 的功能名Net-Framework-Core塞给了 DISM。解决:对照着改,DISM 用NetFx3,PowerShell 用Net-Framework-Core,路径带空格就整体加引号。

5.2 策略与服务类:0x800F0954

现象:命令带齐了/Source和/LimitAccess,仍然返回 0x800F0954,日志里能看到「正在联系 WSUS」之类的字样。原因:组策略把可选组件安装重定向到了 WSUS,本地的/Source被策略层覆盖。域内机器尤其常见。解决:按第 4 章的办法,在「指定可选组件安装和组件修复的设置」里启用并指定备用源,勾选「从不尝试从 Windows 更新下载负载」,gpupdate /force后重启wuauserv。临时机器可以直接改注册表那两个键,改完再跑一次 DISM。

5.3 装完仍报错类:SQL Server 说还缺 3.5、IIS 报 500.19

现象一:NetFx3显示已启用,注册表Install也是 1,但 SQL Server 安装程序的规则检查仍旧提示缺少 .NET Framework 3.5 SP1。原因:多数情况是装完没重启,CBS 里还挂着 pending 操作,安装程序读到的是一致性快照;少数情况是机器上装了 4.x 之后又手工删过 3.5 的注册表键,状态不一致。解决:先重启,再reg query "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5"看Install和语言子键(1033或2052)里的Install是否都为 1。都对还报错,就把 SQL Server 安装程序关掉重开——它启动时会缓存一次环境检查结果,不重开读不到新状态。

现象二:3.5 装好了,IIS 里的老站点起来报 500.19,错误信息指向system.web.extensions之类的配置节。原因:只启用了Net-Framework-Core,没启用Web-Asp-Net,IIS 没有对应的处理程序映射。解决:Install-WindowsFeature -Name Web-Asp-Net -Source D:\sxs,然后在 IIS 管理器里确认「ASP.NET」的 ISAPI 筛选器已注册。如果站点用 WCF,WCF-HTTP-Activation也要一并启用,缺了它站点能起但一调接口就 404。

6. 装完怎么验证:三条命令加一个编译动作

功能状态显示「已安装」不算数,我吃过这个亏——有一次源里的 cab 是残缺的,DISM 报告成功,功能列表也显示启用,结果站点一跑就报FileLoadException。所以最后一关必须落到「能真正跑起一段 CLR 2.0 的代码」。

先看功能层:

验证项命令通过标准
功能状态dism /online /get-featureinfo /featurename:NetFx3状态为「已启用」
注册表reg query "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5" /v Install值等于0x1
PowerShellGet-WindowsFeature Net-Framework-CoreInstallState为Installed

三层都对上,再做实际编译。用 3.5 自带的编译器编一个最小程序,能编出来并运行,说明运行时和引用程序集都齐了:

# 写一个最小 C# 源文件 @' using System; class P { static void Main() { Console.WriteLine(Environment.Version); // 期望输出 2.0.50727.x } } '@ | Set-Content -Path C:\temp\ver.cs -Encoding UTF8 # 用 .NET 3.5 的 csc 编译,注意路径是 v3.5 不是 v4.0.30319 C:\Windows\Microsoft.NET\Framework\v3.5\csc.exe /out:C:\temp\ver.exe C:\temp\ver.cs # 运行,看 CLR 版本 C:\temp\ver.exe

这里的关键是Environment.Version的输出。如果它打出2.0.50727.x,说明程序确实跑在 CLR 2.0 上,3.5 这一套装完整了;如果打出4.0.30319.x,说明编译或运行时被重定向到了 4.x,通常是csc.exe路径写成了v4.0.30319目录。这个细节比看功能列表靠谱得多,因为它验证的是「编译器 + 引用程序集 + 运行时」整条链。

最后一关回到原始场景:把 SQL Server 安装程序重新启动,跑到「安装程序支持规则」那一步。只要那一页不再出现 .NET 3.5 的红叉,后面的组件选择就能正常往下走。这一步别急着点下一步,先在规则页停一下,把「警告」也翻一遍,有些警告是 3.5 装完后才会暴露的(比如 WMI 提供程序注册不全),早发现比装到一半回滚省事。

提示:验证通过后把D:\sxs留着,别急着删。后面打补丁、加 IIS 功能、装报表组件,随时会再向你要一次源文件。我一般会在这个目录里放一个source.txt,写上来源版本和放入日期,半年后再看这台机器不至于一脸茫然。

从那以后我每次给服务器补 .NET 3.5,都强制走一遍「注册表查状态 → 编一个 hello 程序看 CLR 版本 → 重跑安装程序规则页」这套动作,不管 DISM 报得多漂亮。功能列表会骗人,编译器和运行时不会。希望这台机器的坑,你不用再踩一遍。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询