SUMTEC -- There's a thing in my bloglet.

But it's not only one. It's many. It's the same as other things but it exactly likes nothing else...

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
这两天发生一件事:有人在一台64位操作系统的IIS上面部署了一个Web应用,结果一访问就显示出错。出错信息如下:

 

Server Error in '/' Application.

--------------------------------------------------------------------------------
Could not load file or assembly 'MynetMonitorCore' or one of its dependencies. 试图加载格式不正确的程序。 
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 
Exception Details: System.BadImageFormatException: Could not load file or assembly 'XXXXX' or one of its dependencies. 试图加载格式不正确的程序。

这是一件很奇怪的事情,因为我们的上线流程都会在内网部署一个Alpha版本,测试通过了才会正式的上限。其实本来也没有什么大问题,因为这并非一个关键业务,不上线也不会死人。可坏就坏在试图解决该问题的人光凭感觉和Google来解决问题,而没有经过自己的逻辑思考,最后导致整个服务器的所有IIS应用全部挂掉。他是怎么解决的呢?步骤如下:

 

在Google中搜索:“试图加载格式不正确的程序”,于是会出来一大堆的解决方案,大部分都讨论到平台类型的问题,甚至有的提到了32位和64位。我这里就节选当时的第一条搜索结果如下所示:

 

试图加载格式不正确的程序。 (异常来自HRESULT:0x8007000B) - 洛阳知道 ...

2009年8月26日 ... 导致这种错误的原因是,你的dll文件是在64位机下编译的,而你的服务器是32位机,所以无法调用。 下面是别人写的关于解决类似问题的方案,我没有解决。

www.0379zd.com/news/show/36017.htm - 网页快照 - 类似结果 

 

我们内网系统是32位的,而公网的这一台服务器却是64位的,会不会与此有关系呢?确实有关系。

 

嗯,很多人解决问题(包括改Bug)就是这样的——搜索之,然后看到确实有我指定的关键字,或者看起来差不多描述的是类似现象,而结论又说可以解决,于是乎就按照里面说的步骤开始执行。可是为什么会出现这个现象,对方遇到该问题的原因是否和你又是一回事,根本就没想过。更惨的是,有些东西看不懂,于是就不打算看懂而直接忽略了。如果真仔细看这个文章,一定会发现关键的线索的(后面再说)。这位仁兄估计没看懂,于是就继续看后面那几条搜索结果。然后看到如下一条,两眼发光——这不就是我遇到的现象么,简直一模一样:

 

试图加载格式不正确的程序--在64位OS下运行32位项目- kitleer的日志 ...

2010年3月1日 ... 原因:程序集之间的通讯要么全是64位环境下的,要么全是32位环境下的。不能混编访问。不然会出现“试图加载格式不正确的程序”的错误。 ...

kitleer.blog.163.com/blog/static/9177857920102115045194/ - 网页快照 

 

我这里进行部分的摘录,以便于大家阅读:

 

原因:程序集之间的通讯要么全是64位环境下的,要么全是32位环境下的。不能混编访问。不然会出现“试图加载格式不正确的程序”的错误。 

……

B:建立的是“网站”:只需要修改IIS的配置就可以了。

i).“cscript %systemdrive%\inetpub\adminscripts\adsutil.vbs set w3svc/appPools/enable32bitapponwin64 1”

(enable32bitapponwin64为1代表可运行32位应用程序),该项可以通过 “命令行”执行一次。(此命令的作用是使IIS能够注册32位的.net FW)

ii).64位OP默认是在IIS下注册了64位的.net Framework的,因此还必须再注册一个32位的.net Framework

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727>  aspnet_regiis.exe -i 

 

太好了,连解决方案的步骤都有了,这么详细。这位仁兄就照此执行了,可是结果大大出乎所料——问题不仅没有解决,而且所有Web站点都挂了。为什么会挂呢?原因很简单,因为IIS里面的Web服务扩展禁用了32位的ASP.NET v4。因此即便执行了v4.0.30319\aspnet_regiis -i,也不可能启动该应用,而原本就在64位下执行得好好的所有应用也就突然都挂了。实际上这些ASP.NET都已经安装过,再执行一遍aspnet_regiis根本就不会产生任何改变。其实给出这种方案的文章,其作者要么不知甚解,要么就没有好好把问题的成因给描述清楚,实在有点误导的嫌疑。 

 

实际上这个问题是如何造成的呢?如果你仔细读了第一篇文章,说的是PInvoke调用的目标Dll指定了32位平台,而自己写的程序编译时指定了64位平台。稍微思考一下,就会发现这是因为你这堆Dll/Exe当中,存在了目标平台不一致的情况。可是.NET不是号称跨平台、与CPU无关的吗?如果引用的Dll其它没有指定平台,也没有PInvoke,我们一般情况下用C#写的纯托管程序编译成exe之后,无论丢到32位还是64位的机器上,不都是应该能跑的吗?原因其实很简单:mscorlib.dll需要执行PInvoke(或者别的什么原因,没有仔细深究),这一个dll本身是和平台有关的。只不过不同的机器部署了不同的版本,甚至同时部署32位和64位的版本,于是你的平台无关的程序会自动选择并加载合适的mscorlib。因此,如果你编译的时候指定了目标平台,而最终却被强制选择在另一个目标平台下运行(如ASP.NET),就会出现“试图加载格式不正确的程序”这样的错误。

 

这个我们用Reflector一看就知道了:

 

 

上图中的mscorlib的平台就是x64的,而你编译出来的程序一般情况下应该是这样的:


 

也就是平台无关的。一般来说,只要你不使用非托管代码,不是用C++/CLR,并且不选择编译的目标平台类型,那都应该长上面那样。可想而知,如果你选择了x86的目标平台(嗯,我们这边产生该问题就是因为不小心编译成x86平台的),然后丢到一个使用64位的IIS/ASP.NET上面去运行,一定会出错——x86的目标代码试图加载x64的mscorlib自然是不行的。如果你和我们一样,只是因为手哆嗦了一下选了x86作为目标平台,而不是因为什么别的原因,那么解决的方法应该是将Target Platform改成Any再编译一次。而不是像这位仁兄一样,把IIS改成32位,这就本末倒置了。

 

动手之前,想想为什么。 

posted on 2011-01-27 16:04  Sumtec  阅读(6853)  评论(24编辑  收藏  举报