简介:这份面向 Windows Server 2012 R2 云服务器运维人员的 .NET Framework 3.5 离线安装文件包,针对系统提示“找不到源文件”或要求指定还原功能文件位置的常见故障而整理,适用于在线启用失败、云主机镜像精简或系统更新受限、需要本地源还原功能的场景。压缩包共 1568 个文件,约 99.19MB,以 dll、resx、exe、aspx、config、sql 等类型为主,包含运行库程序集、资源文件、安装程序、页面控件、配置模板与数据库脚本,目录层级较完整,便于按组件核对完整性并定位缺失内容。已有 10328 人学习下载,说明该文件集合在同类服务器环境中具有较高参考度。读者可据此获得较完整的离线源文件集合,用于排查源路径不可达、组件缺失与版本不匹配等问题,减少反复搜索零散补丁的时间,也可为 SQL Server 等依赖 .NET 3.5 的部署环境提供排错参考。
1. 从报错到装完只差一个 sources\sxs:Server 2012 上的 .NET 3.5 为什么总装不上
服务器管理器里勾上「.NET Framework 3.5」,进度条走到一半,弹出 0x800F0906,重试十次结果一样——这个画面在 Windows Server 2012 / 2012 R2 上出现的频率,比很多人想象的高得多。它不是系统坏了,也不是权限问题。NetFx3 在 Server 2012 这一代里是按需功能,系统默认不预装,微软也没有为它提供那种双击就能跑的独立安装包;安装程序必须回到安装介质的 sources\sxs 目录里取载荷。取不到,就报下载失败。settled_.net3.5install.zip 走的是离线源这条路,把该在的那份载荷和可复现的命令一起打包。下面从包的结构讲到 DISM 安装、SQL Server 前置检查,再到几个让人反复栽进去的坑。
2. 拆开 settled_.net3.5install.zip:离线包的结构与三条安装路径
拿到压缩包先别急着解压到桌面就点 setup。这一代没人给你写 setup。包里真正起作用的只有一块内容:与目标系统版本匹配的 sources\sxs 载荷目录,其余是说明和命令清单。理解这一点,后面所有命令才有落点。
2.1 包里真正起作用的是哪部分
sources\sxs 这个名字来自 Windows 安装介质本身。Server 2012 的 ISO 里,sources 目录下有一个 sxs 子目录,存放的是可选功能的按需载荷,NetFx3 就在里面。所谓「离线安装包」,本质就是把这段目录从 ISO 里摘出来单独给你,省得每次都要挂载几个 G 的镜像。
这里有两个容易被忽略的硬约束。
第一,载荷和系统版本是绑定的。2012、2012 R2、2016 各自的 sxs 不能互换。你在 2012 R2 上拿 2012 的 sxs 去装,DISM 不会告诉你「版本不对」,它只会给你一个 0x800F081F,看起来像路径写错了,实际是内容对不上。
第二,网上流传的 dotnetfx35.exe 在 Server 2012 上基本走不通。那个包是为客户端系统准备的,服务端这一代走的是功能启用模型,安装程序会直接判定环境不支持。所以别在搜索引擎里抓一个 exe 回来试,方向从一开始就偏了。
包里的内容按用途可以这样理解:
| 内容 | 作用 | 使用时机 |
|---|---|---|
| sources\sxs 载荷目录 | DISM / Install-WindowsFeature 的离线源 | 安装阶段 |
| 命令清单/说明 | 给出可复制的 DISM 与 PowerShell 命令 | 安装阶段 |
| 错误码对照 | 出错后定位是源问题还是策略问题 | 排查阶段 |
解压后先确认 sxs 目录里确实有 NetFx3 相关文件,而不是只有一个空壳目录。这个动作十秒钟,能省掉后面半小时的困惑。
2.2 DISM、PowerShell 与服务器管理器:三条路的取舍
同一个功能,三个入口,底层都走 CBS(组件服务)。选哪条路不影响结果,但影响你出错时能看到多少信息。
| 方式 | 入口 | 能否指定离线源 | 适合场景 |
|---|---|---|---|
| DISM | dism /online /enable-feature | 能,/Source参数 | 远程会话、图形界面进不去、需要精确控制 |
| PowerShell | Install-WindowsFeature | 能,-Source参数 | 批量、写进部署脚本、要拿返回值判断 |
| 服务器管理器 | 添加角色和功能向导 | 能,但入口藏得深 | 单机、有人值守、不想记命令 |
说「入口藏得深」不是夸张。向导一路下一步到「确认」页面,那里有一行不起眼的链接叫「指定备用源路径」,点开才能填 sxs 目录。绝大多数人栽在这一步:勾了功能、点了安装、向导默默去连 Windows 更新、连不上、报 0x800F0906,然后以为是系统问题。
三条路里我一般优先用 DISM,原因是它的输出最直白:成功就是「操作成功完成」,失败会带错误码,日志落在固定位置。PowerShell 的优势是能拿到对象化的返回值,适合写进自动化流程。服务器管理器只在前两种都不可用时才考虑。
还有一点值得提前说清楚:不要试图「手动拷贝 DLL」绕过安装。把 mscorlib.dll 之类的东西复制到 Framework 目录,文件是躺在那儿了,但 CBS 里的功能状态仍然是「未安装」,SQL Server 的前置检查照样拦你。功能启用是一个登记动作,不是文件复制。
3. 用 DISM 指定 /Source 安装:从挂载镜像到 NetFx3 生效
这一章是完整可复现的路径。从确认环境到验证结果,每一步都给命令和判断依据。
3.1 前置检查:系统版本、镜像版本、可用空间
先确认三件事,缺一件后面就可能白跑。
系统版本用一行 PowerShell 拿:
# 取当前系统名称与内部版本号,用来和安装介质比对 Get-CimInstance Win32_OperatingSystem | Select-Object Caption, Version, BuildNumberCaption告诉你这是 2012 还是 2012 R2,BuildNumber是更精确的判据。记下这两个值,等会儿要和镜像对。
再看安装介质。把 ISO 挂载后,确认 sources\sxs 存在且非空:
# 假设镜像挂载到 D:,检查离线源目录 $sxs = "D:\sources\sxs" Test-Path $sxs (Get-ChildItem $sxs -Filter "*NetFx3*" -ErrorAction SilentlyContinue).Count第二行返回 True 只说明目录在,第三行返回大于 0 才说明载荷在。两个条件都满足,这个源才算可用。
系统盘可用空间也要看一眼。NetFx3 装完占几百 MB 量级,如果系统盘只剩一两个 G,安装中途可能因为写不下而失败,错误码还不一定指向空间。
最后确认当前状态,避免重复安装:
# 查询 NetFx3 当前功能状态 Get-WindowsFeature -Name NET-Framework-CoreInstall State显示Installed就别再折腾了,问题在别处。
3.2 三条命令走完安装
环境确认完,正式安装。DISM 这条命令是核心:
# /online 针对运行中的系统;/enable-feature 启用功能 # /featurename:NetFx3 指定目标功能 # /all 连带启用该功能的父功能 # /source 指向离线载荷目录 # /limitaccess 阻止 DISM 回头去连 Windows 更新 dism /online /enable-feature /featurename:NetFx3 /all /source:D:\sources\sxs /limitaccess五个参数里,/limitaccess最容易被漏掉,但它在内网环境里几乎是必须的。不加这个参数,DISM 会先尝试在线获取,等超时才回落到本地源,整个过程可能耗掉十几分钟,最后还是失败。加上它,行为就变成「只用你给的源,拿不到就立刻报错」,排查起来干脆。
PowerShell 的等价写法:
# -Source 指定离线源,不写则会去 Windows 更新拉取 $result = Install-WindowsFeature -Name NET-Framework-Core -Source "D:\sources\sxs" -ErrorAction Stop $result.Success $result.RestartNeeded-ErrorAction Stop让失败直接抛异常而不是静默返回,配合$result.Success可以在脚本里做判断。RestartNeeded单独拿出来看,它是后面验证环节的关键。
如果坚持用服务器管理器向导,路径是:添加角色和功能 → 基于功能安装 → 勾选「.NET Framework 3.5 功能」→ 一路下一步到「确认」页 → 点「指定备用源路径」→ 填入D:\sources\sxs→ 安装。少点那一行链接,前面全白做。
3.3 验证安装结果与读日志
命令返回成功不等于功能真的可用。三重验证,一个都不能省。
第一重,功能状态:
# 功能列表里的状态应为 Installed Get-WindowsFeature -Name NET-Framework-Core # DISM 视角下的详细状态 dism /online /get-featureinfo /featurename:NetFx3第二重,注册表登记:
# Install=1 且 SP=1 才算 3.5 SP1 完整登记 Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5" -Name Install, SP第三重,实际跑一个依赖 3.5 的程序。编译一个最简单的控制台项目,或者直接启动 SQL Server 安装程序看它是否还拦你。前两重是「登记」,第三重是「可用」,两者偶发不一致,尤其在需要重启的场景下。
失败时看日志,位置固定:
# DISM 日志里筛出错误码和 NetFx3 相关行 Select-String -Path "$env:WINDIR\Logs\DISM\dism.log" -Pattern "0x800f", "NetFx3" | Select-Object -Last 40 # CBS 日志更深,一般在 DISM 日志指向具体组件时才需要翻 Select-String -Path "$env:WINDIR\Logs\CBS\CBS.log" -Pattern "NetFx3" | Select-Object -Last 40先看 DISM 日志的最后 40 行,错误码基本都在那里。CBS 日志信息量大但噪音也多,只有在 DISM 日志指向某个具体组件失败时才值得往下钻。
4. SQL Server 前置条件:把 .NET 3.5 排在数据库安装之前
关键词里带 SQL Server 不是偶然。这一代 SQL Server 在 Server 2012 上安装时,前置检查会明确要求 NetFx3,而它的检测方式和你想的不太一样。
4.1 为什么 SQL Server 的检测绕过了 DISM
SQL Server 安装程序的前置检查读的是系统功能登记状态,不是文件是否存在。这带来两个后果。
第一,手动拷贝文件无效。有人觉得「不就是几个 DLL 吗」,从别的机器复制 v3.5 目录过来,文件管理器里看着齐全,SQL Server 的检查照样红叉。原因是 CBS 里那条功能记录还写着未安装。
第二,部分安装无效。DISM 返回成功但RestartNeeded为 True 时,功能处于「挂起」状态,登记尚未完成。这时候启动 SQL Server 安装程序,前置检查大概率仍然失败。正确做法是先重启,再复查功能状态,确认Installed之后再往下走。
所以顺序上有个明确建议:操作系统装完、补丁打完、NetFx3 启用并重启验证通过,再动 SQL Server 的安装介质。反过来做,等于给自己制造一个「数据库装到一半失败、回滚、重装」的循环。
4.2 WSUS 与组策略环境下的安装方式
域环境里最常见的翻车点不在 DISM 命令本身,而在组策略。表现是:命令参数完全正确,源路径也对,但 DISM 卡在「正在搜索更新」,几分钟后报 0x800F0954。
原因是有两条策略在起作用。一条把 Windows 更新的入口指向内网 WSUS,另一条控制可选组件的安装来源。DISM 即便加了/limitaccess,在某些策略组合下仍会被拦。
先查现状:
# 查 WSUS 是否接管了更新入口 reg query "HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" /v UseWUServer # 查可选组件安装是否被策略指定了来源 reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing" /v LocalSourcePath reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing" /v RepairContentServerSourceUseWUServer返回 1,说明更新入口被接管。这种机器上在线安装必然失败,只能走离线源。LocalSourcePath有值则说明策略已经指定了一个源路径,如果那个路径不可达,安装同样失败。
处理方式分两种。临时处理是安装期间把相关策略调整为未配置,装完恢复;正规处理是让域管理员针对这批服务器做一条例外,把可选组件安装来源指向本地 sxs 目录。前一种适合救急,后一种适合批量。
还有一种情况:UseWUServer为 1 但LocalSourcePath为空,这时 DISM 会尝试通过 WSUS 获取载荷,而 WSUS 通常不承载这类按需功能,结果就是超时。这类机器不加/limitaccess的话,表现是「卡很久然后失败」,加了之后是「立刻失败」,后者反而好排查。
4.3 无人值守安装脚本
批量部署时把安装写进脚本,判断条件要写足,不能只调一条命令就完事。
# 离线安装 NetFx3 的可复用片段 $sxs = "D:\sources\sxs" # 条件一:源目录存在 if (-not (Test-Path $sxs)) { throw "离线源路径不存在:$sxs" } # 条件二:目录里有 NetFx3 载荷 if ((Get-ChildItem $sxs -Filter "*NetFx3*" -ErrorAction SilentlyContinue).Count -eq 0) { throw "离线源里没有 NetFx3 载荷,检查镜像版本是否与系统匹配" } # 条件三:当前未安装,避免重复动作 $state = (Get-WindowsFeature -Name NET-Framework-Core).InstallState if ($state -eq "Installed") { Write-Output "NetFx3 已安装,跳过" return } # 执行安装,失败直接抛出 $r = Install-WindowsFeature -Name NET-Framework-Core -Source $sxs -ErrorAction Stop Write-Output "Success=$($r.Success) RestartNeeded=$($r.RestartNeeded)" # 需要重启时给出明确信号,交给上层调度 if ($r.RestartNeeded -eq "Yes") { Write-Output "需要重启后功能才最终生效" }三个前置判断对应三类最常见的失败:路径错、源版本不匹配、重复执行。RestartNeeded单独输出,是因为批量场景下重启往往由上层调度统一处理,脚本自己不该擅自重启服务器。
5. 避坑与排查:五条反复踩到的安装问题
错误码是最快的线索。先看对照表,再看具体案例。
5.1 错误码对照:从 0x800F0906 到 0x800F0954
| 错误码 | 字面含义 | 实际最常见原因 |
|---|---|---|
| 0x800F0906 | 无法下载源文件 | 没指定/Source,或指定了但没加/limitaccess |
| 0x800F081F | 找不到源文件 | 路径写错、镜像版本不匹配、sxs 目录不完整 |
| 0x800F0954 | 被策略阻止 | WSUS 接管更新入口、可选组件来源被策略锁定 |
| 0x800F0907 | 功能安装被禁用 | 组策略明确禁止安装可选组件 |
| 0x800F0922 | 系统分区空间不足 | 系统盘剩余空间不够写入载荷 |
这张表能覆盖九成场景。剩下的一成通常不是单一错误码,而是「命令返回成功但功能不可用」,那属于第 3.3 节验证环节要处理的问题。
5.2 五个具体踩坑记录
第一条:报 0x800F081F,源路径明明是对的。现象是D:\sources\sxs在文件管理器里能打开,里面也有文件,但 DISM 就是报找不到源。原因是镜像版本和系统版本不匹配——2012 R2 的机器挂了 2012 的 ISO,目录结构看着一样,载荷内容对不上。解决方式是回到 3.1 节,用BuildNumber比对镜像版本,换匹配的介质。这一条最容易误判,因为错误码的字面意思指向路径,实际指向内容。
第二条:指定了/Source仍然卡在「正在搜索更新」,最后 0x800F0954。现象是命令跑起来后没有任何输出,几分钟后失败。原因是组策略把更新入口指向了 WSUS,DISM 在尝试走在线通道。解决方式是先按 4.2 节查UseWUServer,确认被接管后,要么加/limitaccess切断在线尝试,要么在安装窗口内临时调整策略。只加参数不查策略,可能仍然失败。
第三条:向导里勾了功能,安装到一半报错,重试无效。现象是服务器管理器一路下一步,最后报 0x800F0906。原因是向导默认走 Windows 更新,没有点「确认」页面那行「指定备用源路径」。解决方式是重新走一遍向导,在确认页点开链接填入 sxs 路径。这条坑的隐蔽之处在于,那个链接的视觉权重极低,很多人做到最后一步就直接点安装了。
第四条:DISM 返回「操作成功完成」,但 SQL Server 前置检查仍然红叉。现象是功能查询显示安装成功,注册表里Install也是 1,但 SQL Server 安装程序坚持说缺 .NET 3.5。原因是功能处于挂起状态,RestartNeeded为 True,登记尚未最终完成。解决方式是重启服务器,重启后重新跑一遍 3.3 节的三重验证,确认状态稳定后再启动 SQL Server 安装。不重启就反复重装,只会让 CBS 里堆一堆中间状态。
第五条:sxs 从网络共享直接读取,安装极慢或中途失败。现象是把 sxs 放在\\fileserver\share\sxs,DISM 能启动但进度条几乎不动,最后报错。原因是 DISM 以系统账户身份运行,访问 UNC 路径时的凭据上下文和当前登录用户不同,权限或网络稳定性都可能出问题。解决方式是把 sxs 复制到本机磁盘再安装,路径尽量短、不要带空格和中文。复制几百 MB 花不了一分钟,比排查网络权限快得多。
6. 把离线包变成可复用资产:校验、固化与一次脚本化
单个服务器装完就结束,那是救火。真正省时间的是把这套流程固化下来,下次换台机器直接跑。
6.1 安装前的版本校验清单
每次动手前过一遍这张表,比事后翻日志便宜得多。
| 检查项 | 命令或方法 | 通过标准 |
|---|---|---|
| 系统版本 | (Get-CimInstance Win32_OperatingSystem).BuildNumber | 与镜像版本一致 |
| 镜像 sxs 完整性 | 统计目录下 NetFx3 相关文件数 | 大于 0 |
| 当前功能状态 | Get-WindowsFeature NET-Framework-Core | 未安装才继续 |
| 更新入口是否被接管 | 查UseWUServer | 被接管则必须用离线源 |
| 系统盘可用空间 | Get-PSDrive C | 留出足够余量 |
这张表的价值不在「知道要查什么」,而在「每次都查」。跳过任何一项,出问题时你就少一个已知条件。
6.2 一段可直接复用的安装函数
把第 4.3 节的片段包成函数,加上日志落盘,方便批量执行时回溯。
function Install-NetFx3Offline { param( [Parameter(Mandatory = $true)][string]$SourcePath, [string]$LogPath = "C:\temp\netfx3_install.log" ) # 确保日志目录存在 $logDir = Split-Path $LogPath -Parent if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir -Force | Out-Null } # 前置校验 if (-not (Test-Path $SourcePath)) { throw "源路径不存在:$SourcePath" } if ((Get-ChildItem $SourcePath -Filter "*NetFx3*" -ErrorAction SilentlyContinue).Count -eq 0) { throw "源路径中没有 NetFx3 载荷" } # 执行并记录结果 try { $r = Install-WindowsFeature -Name NET-Framework-Core -Source $SourcePath -ErrorAction Stop $line = "{0} Success={1} RestartNeeded={2}" -f (Get-Date -Format s), $r.Success, $r.RestartNeeded Add-Content -Path $LogPath -Value $line return $r } catch { Add-Content -Path $LogPath -Value ("{0} FAILED {1}" -f (Get-Date -Format s), $_.Exception.Message) throw } }调用时只传源路径:Install-NetFx3Offline -SourcePath "D:\sources\sxs"。日志里同时记录成功和失败,批量跑完之后 grep 一下就能定位问题机器。
这里有个细节值得说:函数不负责重启。RestartNeeded只作为返回值吐出去,由调用方决定什么时候重启。自动化流程里最忌讳的就是脚本擅自重启生产服务器。
6.3 一次栽跟头之后留下的习惯
早些年我在一台已经装好 SQL Server 的机器上补装 NetFx3,DISM 返回成功,我没重启就去验证 SQL Server,一切正常,于是收工。第二天应用侧反馈某个报表组件报错,翻了半天日志才发现 NetFx3 的功能登记还停在挂起状态,前一天的「正常」只是碰巧没触发到依赖路径。
从那以后我每次装完 NetFx3 都强制走一遍三重验证:功能状态、注册表登记、实际启动一个依赖 3.5 的程序,三步全绿才把机器交出去。多花五分钟,省掉一次跨天的排查。希望帮到你。
本文还有配套的精品资源,点击获取