|
去年冬天,停止我在一个客户现场蹲了整整两天,工作就为了一个弹窗。停止那是工作个老旧的ASP.NET Web Forms项目,部署在Windows Server 2012 R2上,停止IIS 8.5。工作每次访问某个特定页面,停止浏览器没崩,工作服务器没崩,停止但屏幕右下角冷不丁冒出一句:“aspnet version已停止工作”。工作点掉之后页面还能继续用,停止但过几分钟又来一次。工作客户IT负责人站在我身后,停止语气平静但眼神焦虑:“这玩意儿到底怎么回事?工作” 这个提示不是IIS报的,也不是停止.NET Framework本身抛的。它来自Windows错误报告(WER)——一个叫`aspnet_wp.exe`或者`w3wp.exe`的进程崩溃后,系统弹出来的。注意看名字,`aspnet version`其实是个不完整的进程描述,完整路径通常是`C:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_wp.exe`。老版本IIS 5/6的隔离模式才用`aspnet_wp.exe`,IIS 7以后默认是`w3wp.exe`。所以一看到这个弹窗,基本可以判断:要么应用池跑在经典模式且用了旧版.NET,要么是某个模块在CLR层面直接炸了。 我那次遇到的情况更具体。客户用的第三方报表控件,版本号很老,在.NET 4.0下调用了一个已废弃的`System.Web.UI.WebControls`内部方法。平时没事,但报表数据超过两千行时,控件内部递归生成HTML的循环会触发栈溢出——不是内存溢出,是线程栈爆了。CLR遇到栈溢出不会抛`StackOverflowException`让你catch,它直接终止进程。WER捕获到`w3wp.exe`异常退出,就弹了那个“aspnet version已停止工作”。 怎么确认?别去翻事件查看器里的“应用程序”日志,那里往往只有一条模糊的“应用程序池xxx被自动禁用”。直接看Windows日志下的“应用程序错误”子项,找`w3wp.exe`的错误模块。我那次看到错误模块是`clr.dll`,异常代码`c00000fd`——这就是栈溢出。再配合ProcDump抓崩溃转储,用WinDbg加载`clr.dll`的符号,`!clrstack`一看,堆栈里全是报表控件的递归调用。证据链闭合。 解决方式分两层。临时方案:把那个页面的数据量限制在1500行以内,或者把报表改成异步分页加载。根本方案:升级控件到支持.NET 4.5+的版本,或者干脆用`HttpHandler`自己拼HTML表格——慢一点,但不会崩。客户选了后者,因为预算不够买新版控件。改完上线,弹窗消失。但代价是那个页面响应时间从1.2秒变成2.8秒。客户说能忍。 如果你手上没有WinDbg,也不想抓dump,还有个笨办法。打开IIS管理器,找到对应应用池,右键“高级设置”,把“启用32位应用程序”改成True试试。有些老控件在64位下栈空间分配不一样,32位反而能多撑一会儿。但这只是拖延,不是修复。另外检查`web.config`里的``,如果项目实际跑在4.5+,这个值不对也会导致CLR加载错误的程序集版本,偶尔触发类似崩溃。改对之后,至少能排除配置层面的干扰。 还有一种情况更隐蔽:不是栈溢出,是`aspnet_wp.exe`被Windows Defender或者某款杀毒软件当成可疑进程干掉了。我见过一次,客户装了某国产安全软件,它的“网页防护”模块会注入`w3wp.exe`,注入失败就强制结束进程。WER弹的也是“aspnet version已停止工作”。把IIS工作进程目录加到杀软白名单,问题消失。所以别一上来就怀疑代码,先看系统日志里有没有`Windows Defender`或第三方驱动的记录。 最后说个反直觉的观察。这个弹窗在Windows Server 2019/2022上几乎见不到了,不是因为问题少了,而是WER默认不再弹UI,改成静默记录。所以如果你在旧系统上频繁看到它,别觉得是系统老了才这样——恰恰相反,新系统只是把崩溃藏起来了。该查的dump还得查,该改的代码还得改。那个客户后来换了新服务器,我特意去事件查看器里翻,果然有同样的`w3wp.exe`崩溃记录,只是没人再被弹窗打扰了。 |