64位环境中.net framework的运行机制探讨

[size=medium]最近在编写.net应用程序时,发现某些平台下无法加载SQLite DLL的问题。[/size]

[size=medium]症状表现为:[/size]

[size=medium]a. 本地Windows 7/8 64bit开发环境完全正常。
b. 某些Windows 7 64bit用户的计算机无法加载System.Data.SQLite.DLL。
c. 极个别Windows XP的计算机无法加载该DLL。[/size]

[size=medium]无法加载DLL时,均报BadImageFormatException异常,甚至直接被Windows关闭而无法采集异常信息。奇怪的是在当前目录包含了这个DLL文件,所以理应能成功加载才是。[/size]

[size=medium]由于各种场景的组合在一起,一时很难判断问题出于何处。起初笔者努力尝试各种方法试图在本机重现,也均告失败。在偶然的机会,笔者删除了本机所有与SQLite相关的组件,问题成功重现。[/size]

[size=medium]绝望之中燃起了一点希望。我发现自己曾经安装了三个与SQLite相关的组件:[/size]

SQLite-1.0.66.0-setup.exe
sqlite-netFx35-setup-bundle-x64-2008-1.0.82.0.exe
sqlite-netFx35-setup-bundle-x86-2008-1.0.82.0.exe


[size=medium]反复多次后确认了以下内容:[/size]

SQLite-1.0.66.0-setup.exe - 无效果
sqlite-netFx35-setup-bundle-x86-2008-1.0.82.0.exe - 无效果
sqlite-netFx35-setup-bundle-x64-2008-1.0.82.0.exe - 成功


[size=medium]看来,用户系统上没有sqlite-netFx35-setup-bundle-x64-2008-1.0.82.0.exe,所以导致运行失败。经仔细检查,这个包在系统GAC中安装了64位DLL,而之前我发布的目录中包含的是32位DLL文件。一切真相大白,将64位版本与32位版本分开发布,问题解决![/size]

[size=medium]到这里,本该可以松口气了,但我心中仍然充满疑惑:[/size]

[b][size=medium](1) 在64位系统上,我的.net应用到底算是32位的还是64位的?

(2) 32位应用程序照理可以在64位系统上正常运行,为什么当前目录的DLL没有加载成功?

(3) 为何以前调用的第三方DLL从没有发生32/64位的问题,只有这个DLL如此特别?

(4) .net的加载DLL的顺序是什么,到底以哪个优先?[/size][/b]

[size=medium]经过一番认真的Google,上述迷团一一破解。

(1) 在64位系统上,.net程序是通常以64位程序运行的。Windows不会启用Wow。这可以从任务管理器中看到(在64位系统中运行的32位程序将以*32或“32位”标记)。[/size]

[size=medium]然后事情并非还如此简单。.net程序目前有以下几种处理器架构:[/size]

Any CPU(处理器无关的纯MSIL代码)
x64(仅64位处理器,又称AMD64)
x86(仅32位处理器)
IA64(仅IA64位处理器)


[size=medium]之前所说的“通常”,即指Any CPU。这样的程序在32位系统上以32位模式运行,在64位系统上以64位模式运行。这时,而标识为特定处理器的,只能在相应的模式上运行。

更详细地,使用corflags工具可以看到以下信息:[/size]

D:\Lib\x64\>corflags System.Data.SQLite.DLL  
Microsoft (R) .NET Framework CorFlags Conversion Tool. Version 2.0.50727.42
Copyright (c) Microsoft Corporation. All rights reserved.

Version : v2.0.50727
CLR Header: 2.5
PE : PE32+
CorFlags : 24
ILONLY : 0
32BIT : 0
Signed : 1


D:\Lib\x86\>corflags System.Data.SQLite.DLL  
Microsoft (R) .NET Framework CorFlags Conversion Tool. Version 2.0.50727.42
Copyright (c) Microsoft Corporation. All rights reserved.

Version : v2.0.50727
CLR Header: 2.5
PE : PE32
CorFlags : 24
ILONLY : 0
32BIT : 0
Signed : 1


[size=medium]ILONLY: 在映像中是否只包含了IL代码。1表示纯IL代码,0表示包含了Native代码。
32BIT: 即使是纯IL代码,在32位与64位环境中运行还是有区别的。这个标记用于区分x86与Any CPU。
PE: 32位为PE32,64位为PE32+。这个用于区分32位与64位映象。[/size]

Any CPU:PE = PE32  and 32BIT = 0
x86: PE = PE32 and 32BIT = 1
x64: PE = PE32+ and 32BIT = 0


[size=medium]由于之前我们试图在64位进程上加载的32位DLL,其ILONLY=0,即包含平台相关代码,因此无法加载而抛出错误。【注:通过现象推测,需要跟踪.net Framework代码证实】

(2) 这个问题通过上述剖析,已经很清楚了。要说明的是,如果想绕过64位DLL带来的麻烦,将主程序也编译成x86而不是Any CPU,也是可以在64位系统上运行的。这时,操作系统将启用Wow模式启动这个进程。

(3) 这个SQLite的DLL混合了IL和Native代码,必须严格地对应平台调用。

(4) .net加载程序集的顺序:

1、 在GAC(Global Assembly Cache)中搜索相应版本的DLL;
2、 配置文件(web.config或app.config);
3、 应用程序(.exe)当前目录下;

但事实上有可能比这个顺序更为复杂,详见:
http://msdn.microsoft.com/en-us/library/aa720133.aspx 及参考文献7。

之前,在我开发环境中,由于本地GAC中已经包含了正确的x64版本,所以能正确运行。但复制到别人的64位机器上时就出问题了。而有些XP机器上安装了错误的小版本,因此也出现加载失败的现象。

最后,补充说明一下GAC的位置:\Windows\assembly。使用“我的电脑”打开这个目录时,看到的是所有.net全局程序集的列表,这是Windows特殊处理后的结果。只有通过命令行才能看到这个目录中分成了GAC_32, GAC_64, GAC_MSIL等几个子目录。这几个目录正是前文所述不同处理器架构的几种DLL。

[b]补充:
关于之前[c]的问题的解决方案:[/b]
后来发现,System.Data.SQLite.DLL版本1.0.66均能正常工作,但1.0.82在某些Windows XP上无法工作。由于官方网站对1.0.82的解释大多关注在x86与x64的区别,我一度仍然以为是处理器架构造成的。最后,通过depends工具查看发现,1.0.82版的DLL引用了MSVCR90.DLL。而出问题的机器上未安装VC2008 Runtime,自然无法找到该DLL了。

解决的办法有两个:使用1.0.66版,或在XP上安装VC2008运行包:
http://www.microsoft.com/zh-cn/download/details.aspx?id=29

由于之前对64位平台应用的理解有限,文中很多内容通过实验推测,有可能和Microsoft官方表述有不一致或者错误的地方。欢迎大家指正![/size]

参考文献:

1、[url]http://msdn.microsoft.com/en-us/magazine/dd727509.aspx[/url]
2、[url]http://blogs.msdn.com/b/gauravseth/archive/2006/03/07/545104.aspx[/url]
3、[url]http://blogs.msdn.com/b/junfeng/archive/2004/08/11/212555.aspx[/url]
4、[url]http://blogs.msdn.com/b/junfeng/archive/2004/09/12/228635.aspx[/url]
5、[url]http://blog.csdn.net/cstod/article/details/4887049[/url]
6、[url]http://stackoverflow.com/questions/6507675/gac-32bit-vs-64bit[/url]
7、[url]http://blog.csdn.net/wangjunhe/article/details/6692194[/url]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值