尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Server 2012 离线装 .NET 3.5:DISM 与 sxs 源

Server 2012 离线装 .NET 3.5:DISM 与 sxs 源 1. 为什么在 Windows Server 2012 上装 .NET Framework 3.5 会变成一道坎如果你最近接手过一批跑在 Windows Server 2012 上的老业务系统大概率会遇到同一个画面Server Manager 里点了「添加角色和功能」勾上.NET Framework 3.5 功能进度条走到一半弹出一句无法完成需要指定备用源错误码0x800f0906或者0x800f081f。窗口关掉重来还是一样。这不是你手抖也不是系统坏了而是 Windows Server 2012 之后微软把 .NET Framework 3.5 从预装组件改成了按需功能Features on Demand默认只留一个占位符真正的文件躺在安装介质的sources\sxs目录里系统不会主动去拿。本文要讲的就是这件事的完整解法怎么判断你的机器适合在线装还是离线装、离线源到底该指向哪个路径、DISM 和 PowerShell 两套命令怎么用、装完怎么验证、失败了怎么从日志里挖出真正原因。内容面向三类人刚入行的运维新人、需要临时救火的老手、以及在做内网老系统维护的开发者。全部步骤都能在离线内网环境复现不依赖任何第三方工具。1.1 先弄清 .NET Framework 3.5 到底是个什么东西很多人把 .NET Framework 3.5 当成一个版本号更大的运行时其实它是个叠加式安装包。它内部包含三层2.0 的 CLR 与基础类库、3.0 的 WPF/WCF/WF、以及 3.5 自己的 LINQ、AJAX、Entity Framework 早期版本等。所以你在C:\Windows\Microsoft.NET\Framework\下面能同时看到v2.0.50727、v3.0、v3.5三个目录但真正跑代码的 CLR 只有一个——v2.0.50727。v3.5目录里大部分是编译器和扩展程序集不是独立的运行时。这个结构决定了两件事。第一Windows Server 2012 自带的是 .NET Framework 4.5它和 3.5 是并行的两套 CLR互不覆盖装了 4.5 不代表能用 3.5。第二3.5 的组件文件和安装介质是强绑定的——语言、版本号、补丁级别对不上DISM 就会拒绝加载。这也是为什么随手找一份别的镜像去当源经常报错。1.2 什么情况下非装 3.5 不可不是所有老程序都需要它但下面这几类是硬需求绕不过去IIS 上的 ASP.NET 2.0 / 3.5 应用池应用池的 .NET CLR 版本一栏如果选v2.0没有 3.5 组件时应用池根本起不来。老版本 SQL Server 的部分管理组件SSMS 早期版本的某些插件依赖 2.0 程序集。第三方商业软件的安装程序不少国产财务、进销存、生产管理软件至今仍在安装包里判断v2.0.50727是否存在。PowerShell 2.0 引擎Server 2012 的 PowerShell 2.0 引擎实际上建立在 .NET 2.0 之上某些老脚本模块加载时会连带检查。自研的 WinForm / WebService 老代码编译目标框架写着.NET Framework 3.5运行时缺组件就报FileNotFoundException。先确认再动手用Get-ChildItem C:\Windows\Microsoft.NET\Framework\看一眼有没有v3.5目录。有目录不等于装好了还得看 Server Manager 里.NET Framework 3.5 Features的状态是不是Installed。1.3 微软为什么要设计按需功能从 Windows 8 / Server 2012 开始微软把很多低频组件从系统盘里挪了出去动机很实在镜像体积小、系统盘占用低、被攻击面小。代价就是——它假设你能随时访问 Windows Update。而现实里内网服务器绝大多数根本连不到外网或者被 WSUS 接管、WSUS 上又没勾选这个产品分类。于是按需就变成了按不了。理解这个设计逻辑后面的排查思路就顺了报错说找不到源文件本质就是系统在 Windows Update 和本地源之间都没找到匹配的sxs包而不是权限或服务的问题。抓住这条主线80% 的坑都能自己定位。2. 动手前的环境盘点与方案选型救火现场最怕的就是直接敲命令。我见过太多人一上来就 DISM报错之后开始百度错误码折腾两小时才发现镜像版本根本不对。花十分钟做盘点能省掉后面两小时。2.1 三行命令摸清家底开一个管理员权限的 PowerShell依次跑# 1. 看系统版本、内部版本号、SKU Get-ComputerInfo | Select-Object WindowsProductName, WindowsVersion, OsBuildNumber, OsArchitecture # 2. 看 .NET 目录情况 Get-ChildItem C:\Windows\Microsoft.NET\Framework\ -Directory | Select-Object Name # 3. 看当前功能状态需要 Server Manager 模块2012 自带 Get-WindowsFeature NET-Framework-Core, NET-Framework-45-Core | Format-Table Name, DisplayName, InstallStateOsBuildNumber很关键。Server 2012 正式版是9200Server 2012 R2 是9600。这两个数字决定了你必须用哪一份安装介质不能混。另外要确认是带 GUI 的完整版还是Server CoreCore 版只能用 DISM/PowerShell没有图形界面。2.2 三条路线的取舍路线适用场景优点风险点服务器管理器图形界面有外网或能访问 WSUS最直观不用记命令无外网时必然报 0x800f0906DISM / PowerShell 在线安装能直连 Windows Update一条命令搞定内网被 WSUS 接管时走不通DISM 指定本地 sxs 源离线内网、完全断网稳定可复现秒级完成镜像版本/语言必须严格匹配我个人的选择习惯是只要能拿到安装介质一律走第三条。原因很简单——在线路径依赖网络质量、WSUS 策略、更新服务器状态三个变量任何一个抖动都要重新排查本地源只有匹配和不匹配两种结果确定性强得多。而且离线安装通常十几秒就结束了在线有时要等好几分钟。2.3 容易被忽略的前置检查几个我踩过坑的地方动手前务必确认磁盘空间。sxs目录提取出来大约 200~300 MB安装完成后系统盘还会多占一部分。C 盘剩余空间低于 2 GB 时DISM 有可能中途失败并给出与空间无关的误导性错误。建议留 5 GB 以上。组策略是否锁死了源路径。查一下这个位置reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate /v WUServer reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing /s如果第一行有输出说明机器被 WSUS 接管了在线安装的请求会打到内网更新服务器如果第二行有LocalSourcePath说明有人配过本地源策略且路径可能已经失效。这两种情况都会覆盖你命令行里传的参数必须先清掉或改对。别用被优化过的镜像。网上流传的一些精简版 Server 镜像会直接删掉sources\sxs目录甚至删掉WinSxS里的组件存储。这种镜像装 3.5 基本无解只能换原版介质。虚拟化环境先打快照。如果是虚拟机动手前拍一个检查点。按需功能安装失败有时会留下半成品状态回滚比清理干净得多。3. 离线安装用安装介质做源的分步实操这一节是全文的核心我把它拆成取源—挂载—执行—验证四步每一步都给出可复制粘贴的命令和判断标准。3.1 从镜像里把 sxs 目录取出来假设你手上是Windows_Server_2012_R2_Chinese.iso版本号和目标机器一致。方法一直接挂载 ISO。在 Server 2012 上可以用 PowerShell 挂载不需要装虚拟光驱$iso Mount-DiskImage -ImagePath D:\ISO\Windows_Server_2012_R2_Chinese.iso -PassThru ($iso | Get-Volume).DriveLetter返回的盘符比如E就是源盘。这个方法最干净虚拟光驱会自动分配盘符用完Dismount-DiskImage卸载即可。方法二把sources\sxs整个拷到本地磁盘。适合介质拔走之后还要重装的情况。Copy-Item E:\sources\sxs -Destination C:\sxs -Recurse -Force拷完之后可以用Get-ChildItem C:\sxs | Measure-Object数一下文件数量。正常的 Server 2012 R2 中文版sxs目录里应该有几十个.cab文件命名形如microsoft-windows-netfx3-ondemand-package~31bf3856ad364e35~amd64~~6.3.9600.16384.cab。注意如果这份镜像不是目标系统的同版本、同语言介质后面的 DISM 会直接报 0x800f081f不要抱侥幸心理。Server 20126.2.9200和 Server 2012 R26.3.9600的包名里的版本号完全不同互换必然失败。3.2 DISM 命令逐条拆解最常用的一条DISM /Online /Enable-Feature /FeatureName:NetFx3 /All /LimitAccess /Source:E:\sources\sxs别急着敲先看每个参数在干什么理解了才能应变/Online操作当前运行的系统而不是离线镜像。/Enable-Feature /FeatureName:NetFx3启用名为NetFx3的功能。注意功能名是NetFx3不是.NET Framework 3.5名字写错会报功能名称未知。/All把该功能的所有父级功能一并启用。3.5 下面挂着 WCF 激活等子项加了这个省事。/LimitAccess明确告诉 DISM 不要去碰 Windows Update。这个参数是离线场景的关键没有它DISM 可能先尝试联网超时后报0x800f0906看起来像源文件有问题其实只是它绕路了。/Source:指定备用源路径指向sxs文件夹本身不是指向 ISO 根目录。如果不想拷文件可以直接从 WIM 里取写法略有不同DISM /Online /Enable-Feature /FeatureName:NetFx3 /All /LimitAccess /Source:wim:E:\sources\install.wim:2这里:2是镜像索引号必须先查清楚DISM /Get-WimInfo /WimFile:E:\sources\install.wimServer 2012 R2 的install.wim通常包含 4 个索引一般规律是奇数为 Core、偶数为带 GUI 的完整版但不要凭记忆猜以实际输出为准。索引写错DISM 会告诉你指定的映像中找不到组件不会损坏系统重试即可。如果介质里的系统映像文件是install.esd而不是install.wim新版客户端镜像常见DISM 同样支持/Source:esd:E:\sources\install.esd:2的写法不需要先转换格式。这一点在更新的 Windows 版本上做离线安装时特别有用。3.3 PowerShell 版本的等价命令习惯 PowerShell 的话Server 2012 的 ServerManager 模块提供了更友好的封装Install-WindowsFeature -Name NET-Framework-Core -Source C:\sxs -Verbose好处是-Verbose会实时打印进度失败时提示也更完整。它内部调用的还是 DISM所以源目录的要求完全一致。如果同一个功能还要带 WCF 激活可以一次写全Install-WindowsFeature -Name NET-Framework-Core, NET-Framework-45-Core, WCF-HTTP-Activation, WCF-NonHTTP-Activation -Source C:\sxs注意NET-Framework-45-Core在新系统上默认已装把它列进去是无害的但如果你的sxs是从纯 2012 介质里取的它可能不含 4.5 的包此时会报错。稳妥做法是先只装NET-Framework-Core装完再单独加其他功能。3.4 怎么确认真的装好了装完别只看命令返回值做三重验证# 1. 功能状态 Get-WindowsFeature NET-Framework-Core | Select-Object Name, InstallState # 2. DISM 视角 DISM /Online /Get-Features | findstr /i NetFx3 # 3. 文件系统视角 Test-Path C:\Windows\Microsoft.NET\Framework\v3.5\mscorlib.dll Test-Path C:\Windows\Microsoft.NET\Framework64\v3.5\mscorlib.dllInstallState显示Installed、NetFx3状态为Enabled、两个路径都返回True这三条同时满足才算真正装完。有时命令返回成功但组件存储里是半成品状态Get-WindowsFeature会显示InstallPending这种情况需要重启再检查一次。再补一个更硬的验证——看注册表里的版本号reg query HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v3.5 /v Version正常应返回类似3.5.30729.4926的值。数字末段会随补丁变化但只要能看到说明 NDP 键值已经写入运行时注册完整。4. 在线安装与图形界面路径的实操细节不是所有场景都能摸到安装介质。有些远程托管的机器、或者客户不愿提供镜像的场合就得在在线路径上想办法。4.1 服务器管理器图形界面的正确打开方式路径是服务器管理器 → 管理 → 添加角色和功能 → 一路下一步到功能页 → 勾选.NET Framework 3.5 功能会连带勾选下面的.NET Framework 3.5 (包括 .NET 2.0 和 3.0)和WCF 激活。到确认页时如果系统检测不到源会出现一行蓝色小字指定备用源路径。这行小字就是救命的入口。点开它填入C:\sxs或E:\sources\sxs再点安装。本质上是给你一个图形化的/Source:参数入口。很多人忽略了这行小字直接点安装然后卡在错误码上。提示图形界面里填写备用源路径的输入框不会做路径有效性校验填错了照样能点安装然后在最后一步失败。建议先在文件资源管理器里确认路径能打开。4.2 用组策略给整台机器指定默认源如果你要批量处理几十台服务器逐台敲命令不现实。这时候用组策略统一指定本地源更合适。策略位置本地组策略或域 GPO 都行计算机配置 → 管理模板 → 系统 → 指定可选组件安装和组件修复的设置。启用后有几个选项备用源文件路径填C:\sxs如果这台机器上放了副本或 UNC 路径\\fileserver\sources\sxs。从不尝试从 Windows 更新下载负载勾上等于全局的/LimitAccess。联系 Windows 更新以修复损坏的组件按需如果WinSxS有损坏可能需要。注册表层面这个策略落在HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Servicing LocalSourcePath REG_EXPAND_SZ C:\sxs UseWindowsUpdate REG_DWORD 2UseWindowsUpdate的值含义是0 从不检查、1 先检查更新再检查本地源、2从不检查更新只用本地源。离线环境填2。用 UNC 路径时有个坑sxs目录的 NTFS 权限必须让Everyone或至少Domain Computers可读而且机器账户要能访问共享。域环境里更稳的做法是把sxs复制到每台机器的本地磁盘或者用 DFS 分发。我遇到过共享权限没问题、但因为 SMB 签名策略不匹配导致 DISM 读不到文件的情况排查了很久。4.3 WSUS 环境下的特殊处理被 WSUS 接管的机器0x800f0906几乎是必然。因为组策略把更新源指到了内网 WSUS而 WSUS 默认不勾选按需功能这类产品分类DISM 去请求时拿回来的是一份空清单。处理方向有两个一是让 WSUS 管理员在产品和分类里把Windows Server 2012 R2及相关更新勾上并同步二是干脆在目标机器上临时改注册表把WUServer策略清掉装完再恢复。第二个做法见效快但改注册表前一定记录原值否则后续补丁策略会乱。# 记录原值务必先做 reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate /s C:\backup\wu-policy.txt更稳妥的第三条路直接用-Source指定本地源并加/LimitAccess这个组合走的是离线修复通道正常情况下会绕过 WSUS。如果还是不行才考虑动策略。5. 常见报错与排查速查错误码是最容易被误导的地方。0x800f0906字面意思是无法下载源文件让人以为是网络问题实际上八成是源路径不对。下面按我这些年遇到的频率排序。5.1 高频错误码对照表错误码真实含义常见根因处理动作0x800f0906无法从更新下载源文件无外网、WSUS 未同步、未加/LimitAccess改用本地sxs源0x800f081f找不到源文件源目录里没有匹配版本的 cab核对系统版本与镜像版本0x800f0907被策略阻止组策略禁用了按需功能检查组策略中的可选项安装设置0x800f0922组件存储事务失败WinSxS损坏、磁盘空间不足跑DISM /Online /Cleanup-Image /RestoreHealth0x800f0954WSUS 返回不适用WSUS 未勾选对应分类请联系 WSUS 管理员或绕行0x80073712组件存储配置损坏补丁安装中断留下的残留用sfc /scannow DISM 修复0x8007000d数据无效cab 文件损坏或下载不完整重新提取介质中的sxs5.2 语言不匹配这个隐形杀手这是我遇到过最容易误判的一类。现象很怪命令一条不差、介质版本也对就是报0x800f081f。最后发现是英文版镜像配中文系统。按需功能的 cab 包名里带语言标识比如~zh-cn~、~en-us~。系统是中文的源里只有英文包匹配不上。判断方法很简单——看源目录里的文件名microsoft-windows-netfx3-ondemand-package~31bf3856ad364e35~amd64~~6.3.9600.16384.cab microsoft-windows-netfx3-ondemand-package~31bf3856ad364e35~amd64~zh-cn~6.3.9600.16384.cab带zh-cn的那一行就是语言包。如果源里只有不带语言标识的语言中立包和en-us而系统是中文就一定装不上。解决办法只有换一份对应语言的安装介质。在这种情况下任何修复工具都帮不了你因为文件本身就不存在。5.3 从日志里挖真正的原因DISM 在控制台只给一个错误码细节全在日志里。两个位置必看C:\Windows\Logs\DISM\dism.log C:\Windows\Logs\CBS\CBS.log搜关键字Select-String -Path C:\Windows\Logs\DISM\dism.log -Pattern 0x800f -Context 3,3 | Select-Object -Last 40CBS 日志信息量更大建议按时间倒着看Get-Content C:\Windows\Logs\CBS\CBS.log -Tail 300 | Select-String NetFx3|ERROR|Failed实操经验是CBS.log 里那一句Failed to find payload会直接告诉你缺的是哪个 cab 文件比错误码有用得多。拿到文件名之后去源目录里 grep 一下能不能匹配一目了然。一个小技巧CBS 日志默认打开详细日志会变得巨大。排查完记得把HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Servicing\LogLevel改回去否则几周后可能吃掉几个 GB。5.4 排查顺序建议按这个顺序走能最短路径定位Get-WindowsFeature NET-Framework-Core看当前状态确认不是已经装了一半。Get-ComputerInfo拿版本号和介质版本比对。看sxs目录里的 cab 文件名确认语言标识。检查组策略中的可选项安装设置确认没有指向失效路径。加/LimitAccess重试一次。还不行就翻 CBS.log 找Failed to find payload。6. 安装之后配置、验证与长期维护装完不等于能用。有三个后续动作不做的话过几天还会出问题。6.1 把 IIS 应用池绑回去3.5 装好只是提供运行时应用池还得显式指向v2.0。图形界面路径IIS 管理器 → 应用程序池 → 选中目标池 → 基本设置 → .NET CLR 版本选v2.0.50727。命令行也可以Import-Module WebAdministration Set-ItemProperty IIS:\AppPools\MyLegacyAppPool -Name managedRuntimeVersion -Value v2.0改完记得iisreset。我遇到过应用池配置改了但没重启 IIS页面照样 500 的情况白排查一小时。另外要确认站点的应用程序池身份对C:\Windows\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files有写权限。这个目录权限被收紧过之后ASP.NET 2.0 应用会报无法生成临时类错误信息完全看不出是权限问题。6.2 版本共存与补丁维护3.5 和 4.x 各自独立更新互不影响。但有个细节要注意Windows Update 里的 .NET 更新是分开列的打补丁时如果只打了 4.x 的累积更新3.5 那部分可能停在旧版本存在安全风险。建议在 WSUS 或补丁管理平台里把NET Framework 3.5相关分类也纳入审批范围。如果这台服务器还承担其他角色用Get-WindowsFeature定期盘一次确认没有因为某次失败的回滚把 3.5 卸掉。我见过一次补丁安装失败自动回滚把NetFx3退回到InstallPending状态应用直接挂掉而服务器本身看起来一切正常。6.3 关于授权这件事得说清楚网上能搜到大量所谓Windows Server 2012 产品密钥清单我要提醒一句**这些清单绝大多数是批量授权泄露出来的很多已被封禁而且使用来源不明的授权本身存在合规风险。**正确的做法是通过正规渠道获取合法授权——OEM 随机版、批量许可协议、或者官方订阅。授权状态可以用slmgr /dlv查看只做只读查询不会改动任何东西。企业环境里授权不合规轻则审计出问题重则整个虚拟化集群被要求整改代价远高于授权本身的成本。6.4 别忘了这台系统本身的生命周期Windows Server 2012 和 2012 R2 的扩展支持已经结束意味着不再有安全更新。这不是要不要升级的问题而是什么时候升级的问题。现实里大量内网老系统因为业务软件不兼容短期内没法迁移那就要靠网络隔离 最小权限 定期快照来兜底把这类服务器放在独立网段只开放必要端口不在上面装浏览器和邮件客户端虚拟化层面定期做检查点。如果确实要迁移评估路径通常是先升到 Windows Server 2016/2019 验证业务软件兼容性再考虑更新版本。迁移前先在测试环境跑一遍本文的流程确认目标系统上的 3.5 安装同样顺利——好消息是这套按需功能的机制一直沿用命令基本不用改。7. 我踩过的坑与实操心得前面讲的是方法论这里把几个只有亲手做过才会知道的细节摊开说。7.1 源目录别放在会被清空的临时位置有次图省事把sxs拷到了C:\Windows\Temp\sxs装完就删了。三个月后另一台机器需要安装翻出当时的操作记录照着做结果报 0x800f081f——因为路径早就不存在了脚本里写了-Recurse把源也一起清了。后来我的习惯是在C:\下固定建一个C:\sxs装完保留并在服务器文档里注明这是按需功能源不要删。占 200 多 MB 换来的是以后任何一台同版本机器都能直接复用。如果管理的是几十台机器更彻底的做法是在文件服务器上放一份用组策略统一指向 UNC 路径。注意共享权限要给到Domain Computers的读权限只给用户组是不够的。7.2 快照不是可选项DISM 装按需功能是事务型操作正常情况回滚很干净。但如果在安装过程中断电、或者被远程强制关机WinSxS里可能留下半提交状态之后所有补丁安装都会报组件存储损坏。修起来比较麻烦DISM /Online /Cleanup-Image /CheckHealth DISM /Online /Cleanup-Image /ScanHealth DISM /Online /Cleanup-Image /RestoreHealth /Source:C:\sxs /LimitAccessRestoreHealth在某些版本上耗时可能超过半小时而且需要联网或指定源。相比之下虚拟机动手前拍一张快照只要几秒钟。这个投入产出比不言而喻。7.3 新版客户端上的同一件事有细微差别如果哪天你在更新的 Windows 客户端版本上遇到同样的问题——比如给某台新装机配上个老式行业软件——流程几乎一样但有两处差异值得提前知道。第一较新的镜像里系统映像文件常常是install.esd而非install.wim需要用/Source:esd:...的写法DISM 是支持的不需要先转格式。第二某些预览性质的系统版本上sxs的组件可能并没有随镜像完整分发或者对应版本尚未提供按需功能包这时候无论怎么指定源都会失败。遇到这种情况不要死磕先用DISM /Online /Get-Features确认NetFx3这个功能名在当前系统上是否还存在再去官方渠道确认该版本是否提供对应的按需功能包。版本越新越要先确认能力是否存在再谈怎么安装。7.4 一条命令的自检脚本最后给一个我常用的自检脚本装在机器上任何一次安装前后都可以跑十秒钟出结果$os Get-ComputerInfo | Select-Object OsBuildNumber, OsArchitecture $feat Get-WindowsFeature NET-Framework-Core $v2 Test-Path C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll $v35 Test-Path C:\Windows\Microsoft.NET\Framework\v3.5\mscorlib.dll $src Test-Path C:\sxs [PSCustomObject]{ 系统版本 $os.OsBuildNumber 架构 $os.OsArchitecture 功能状态 $feat.InstallState CLR2核心 $v2 v3.5目录 $v35 本地源可用 $src } | Format-List把这段存成C:\tools\check-net35.ps1以后每台服务器都跑一遍比在服务器管理器里一层层点开快得多。尤其是本地源可用这一项很多莫名其妙的失败都是因为源路径在某次磁盘清理中被删掉了而人总是习惯性认为它还在。我个人在实际维护这批老服务器的体会是真正让人头疼的从来不是命令本身而是环境的不确定性——不知道版本、不知道语言、不知道策略指向哪里。把这三件事在动手前确认清楚剩下的就是复制粘贴。等到哪天这些机器全部退役这套流程在新版本上依然能直接套用无非是把/Source:后面的路径换一换而已。
返回列表