dll文件32位64位检测工具以及Windows文件夹SysWow64的坑
自从操作系统升级到64位以后,就要不断的需要面对32位、64位的问题。相信有很多人并不是很清楚32位程序与64位程序的区别,以及Program Files (x86),Program Files的区别。同时,对于程序的dll文件应该放到System32文件夹,还是SysWow64,大部分人做的决定是,32位程序放到System32,64位程序放到SysWow64。是不是这样呢,那么今天就由我身边发生的一个案例来详细的说明一下。
dll文件不匹配导致数据库无法启动
前段时间,数据库做了一些功能上的改进,于是用VS2010编译检出了一个版本,供测试部测试。测试部拿到数据库后,通过批处理将数据库程序,注册为服务。虽然执行的是批处理,实际上注册服务的过程,是通过运行数据库程序,并给其传入命令行参数来完成的,详情请看这篇文章玩转Windows服务系列——Debug、Release版本的注册和卸载,及其原理。
通过批处理运行程序后,出现如下问题:

出现这种问题,测试部不淡定了,叫我去看。我又试着运行了一下程序,依然出现这个问题。“可是在我的机器上运行的挺好的啊”,这是我说的第一句话,相信很多人看了这句话就会心的笑了。
有问题就是有问题,既然我的机器上可以正常运行,那么测试机为什么不行呢,首先要查找原因。
数据库是用VS2010编译的,那么在其他机器上运行,就需要运行的操作系统中以及安装了VS2010的运行时,否则就会因为缺少程序运行所必须的dll文件而无法正常运行。我想应该是这个原因,但又一想,如果没有装运行时的话,会提示缺少msvcr100.dll、msvcp100.dll等文件,上图中的问题显然不是缺少dll问题。问题有点复杂,为了简单,先试着安装运行时,看能不能解决吧。
将VS2010的x86和x64 Runtime安装包全装了一遍。再运行程序,依然是这个醒目的错误。
虽然安装运行时没有解决这个问题,但根据经验判断,要么是缺少dll文件,要么就是dll文件版本出了问题。那么,接下来就是想办法证明这个猜想。
通过Dependency Walker检测数据库程序,所有依赖的dll文件都存在,没有发现什么问题。然后通过Windows Sysinternals中的ListDLLs工具检测当前运行的进程已经加载的dll文件,从列表中看到msvcr100.dll没有加载,估计就是这个dll文件出了问题。从我的机器上找到这个文件,替换了测试机上的msvcr100.dll文件后,数据库就正常运行了。
原来,刚刚启动数据库的时候,提示找不到msvcr100.dll文件,测试的同事就从其他的XP系统的机器上找了这个文件,并分别放入到System32和SysWow64中,于是就导致了上图中的这个问题。
由于XP系统是32位的,所以找到的msvcr100.dll文件也是32位,当把这个32位程序放到System32文件夹后,启动64位数据库,就会加载这个32位dll,由于64位程序只能加载64位dll,所以当程序尝试加载32位dll时,就会报错了。
究竟是System32还是SysWow64
Win7、Server2008等64位系统出来以后,为了兼容32位程序,所以采用了Wow64方案,在系统文件夹中,可以看到一个System32文件夹,和一个SysWow64文件夹。虽然这个方案对于程序来说,可以很方便的兼容32位程序,但是对于一般用户来说,想分辨System32和SysWow64那就有点困难了,因为名字太有迷惑性了。
至于微软为什么采用Wow64方案,我就不细说了,感兴趣的朋友可以看这篇文章:什么是SysWow64。这篇文章详细的介绍了Wow64技术,以及64位系统兼容32位程序的情况。
最后,我们可以知道:
- SysWow64文件夹,是64位Windows,用来存放32位Windows系统文件的地方,而System32文件夹,是用来存放64位程序文件的地方。
- 当32位程序加载System32文件夹中的dll时,操作系统会自动映射到SysWow64文件夹中的对应的文件。
看到这些,你一定会认为你真正的明白了System32和SysWow64的区别,我也一样,我以为我真的懂了,但是真的懂了吗,是真懂了吗?
无论怎样还是请你坚持看完。
区分dll文件32位64位的程序让我倍感迷惑
上面说到的数据库无法启动的这种情况,已经遇到了不止一次了。每次遇到这种问题,我都想能不能有个工具可以检查System32和SysWow64文件夹中的dll程序是不是对应的64位和32位程序。据我所知只有dumpbin可以查看一个dll文件是32位还是64位,但它明显不是我想要的工具,因为每次只能查看一个文件。
好吧,自己动手,丰衣足食,既然没有这种工具,那就来写一个吧,好在判断dll文件是32位还是64位也不是很难。
Windows系统下,exe、dll文件都可以称为PE文件,他们有相同的文件格式,称为PE文件格式。
PE文件的第一个部分是IMAGE_DOS_HEADER,大小为64B,对于检查32位64位来说,有一个重要的成员e_lfanew,这个成员的值为IMAGE_NT_HEADERS的偏移。
IMAGE_DOS_HEADER的定义如下:
typedef struct _IMAGE_DOS_HEADER
{//(注:最左边是文件头的偏移量。)
+0h WORD e_magic //Magic DOS signature MZ(4Dh 5Ah) DOS可执行文件标记
+2h WORD e_cblp //Bytes on last page of file
+4h WORD e_cp //Pages in file
+6h WORD e_crlc //Relocations
+8h WORD e_cparhdr //Size of header in paragraphs
+0ah WORD e_minalloc //Minimun extra paragraphs needs
+0ch WORD e_maxalloc //Maximun extra paragraphs needs
+0eh WORD e_ss //intial(relative)SS value DOS代码的初始化堆栈SS
+10h WORD e_sp //intial SP value DOS代码的初始化堆栈指针SP
+12h WORD e_csum //Checksum
+14h WORD e_ip //intial IP value DOS代码的初始化指令入口[指针IP]
+16h WORD e_cs //intial(relative)CS value DOS代码的初始堆栈入口
+18h WORD e_lfarlc //File Address of relocation table
+1ah WORD e_ovno //Overlay number
+1ch WORD e_res[4] //Reserved words
+24h WORD e_oemid //OEM identifier(for e_oeminfo)
+26h WORD e_oeminfo //OEM information;e_oemid specific
+29h WORD e_res2[10] //Reserved words
+3ch DWORD e_lfanew //Offset to start of PE header 指向PE文件头
} IMAGE_DOS_HEADER;
IMAGE_NT_HEADERS的定义如下:
typedef struct _IMAGE_NT_HEADERS
{
+0h DWORD Signature;
+4h IMAGE_FILE_HEADER FileHeader;
+18h IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS;
Signature 字段:在一个有效的 PE 文件里,Signature 字段被设置为00004550h,ASCII 码字符是“PE00”。标志这 PE 文件头的开始。“PE00” 字符串是 PE 文件头的开始,DOS 头部的 e_lfanew 字段正是指向这里。
IMAGE_FILE_HEADER 结构定义:
typedef struct _IMAGE_FILE_HEADER
{
+04h WORD Machine; // 运行平台
+06h WORD NumberOfSections; // 文件的区块数目
+08h DWORD TimeDateStamp; // 文件创建日期和时间
+0Ch DWORD PointerToSymbolTable; // 指向符号表(主要用于调试)
+10h DWORD NumberOfSymbols; // 符号表中符号个数(同上)
+14h WORD SizeOfOptionalHeader; // IMAGE_OPTIONAL_HEADER32 结构大小
+16h WORD Characteristics; // 文件属性
} IMAGE_FILE_HEADER;
其中Machine字段表示可执行文件的目标CPU类型:
- IMAGE_FILE_MACHINE_I386 0x014c x86
- IMAGE_FILE_MACHINE_IA64 0x0200 Intel Itanium
- IMAGE_FILE_MACHINE_AMD64 0x8664 x64
这样不是很直观,上张图来看一下:

有了这些,我们就可以通过程序来判断32位、64位了,代码如下:
public static bool IsPE32(string path)
{
FileStream file = File.OpenRead(path);
//移动到e_lfanew的位置处
stream.Seek(0x40 - 4, SeekOrigin.Begin);
byte[] buf = new byte[4];
stream.Read(buf, 0, buf.Length);
//根据e_lfanew的值计算出Machine的位置
int pos = BitConverter.ToInt32(buf,0) + 4;
stream.Seek(pos, SeekOrigin.Begin);
buf = new byte[2];
stream.Read(buf, 0, buf.Length);
//得到Machine的值,0x14C为32位,0x8664为64位
Int16 machine = BitConverter.ToInt16(buf, 0);
if (machine == 0x14C)
{
return true;
}
else
{
return false;
}
}
最核心的功能完成了,剩下的就是界面和遍历文件夹了,效果图:

根据检测结果和实际情况判断,检测结果没问题。那么就开始真正的检测吧,System32和SysWow64。检测结果如下图:

从图中看出,System32、SysWow64中检测出的所有的文件均为32位程序,根据常识也可以判断出,实际肯定不是这样的。一定是程序出了什么问题,那么直接用十六进制编辑器看一下两个文件是否一致吧。
再次判断究竟是System32还是SysWow64——意想不到的坑
通过UE查看两个文件夹中的msvcr110d.dll确实都是32位程序,而且用Beyond Compare进行比较,两个文件也没有差异。用工具查看两个文件的MD5也是完全一致:

难道两个文件真的都是32位吗,我还是觉得不太可能。
接下来将System32和SysWow64中的msvcr110d.dll分别移动到其他文件夹,这样System32和SysWow64就没有这个dll文件了,然后运行一个32位的需要这个dll文件的程序PeTest,提示找不到这个dll文件。分别将原来System32和SysWow64中的msvcr110.dll拷贝到这个PeTest所在的目录,运行程序。当使用SysWow64中的msvcr110d.dll时,程序可以正常运行,说明这个文件确实是32位。当使用System32中的msvcr110d.dll时,程序无法正常运行,出现文章开始时提到的错误。为什么通过Beyond Compare、UE、MD5检测为同样的dll文件,一个可以正常运行,另外一个就不可以呢。
再次思考这两句话:
- SysWow64文件夹,是64位Windows,用来存放32位Windows系统文件的地方,而System32文件夹,是用来存放64位程序文件的地方。
- 当32位程序加载System32文件夹中的dll时,操作系统会自动映射到SysWow64文件夹中的对应的文件。
32位程序加载System32文件夹中的dll文件,操作系统会自动映射到SysWow64文件夹,也就是说64位程序,系统不会再做映射。
通过任务管理器查看UE、Beyond Compare和MD5三个进程全部为32位进程,即三个程序全部是32位。
至此我们可以重新理解“32位程序加载System32文件夹中的dll文件,操作系统会自动映射到SysWow64文件夹”这句话,应该是“只要32位程序访问System32文件夹,无论是加载dll,还是读取文本信息,都会被映射到SysWow64文件夹”。
这个理解对吗,我们来做一个实验验证一下。
找一个32位的文本编辑器Notepad++(Win7系统自带的是64位),在SysWow64文件夹中新建一个1.txt的文件,打开编辑此文件,内容为SysWow64。然后在打开文件对话框中的输入框中输入“C:\Windows\System32\1.txt”,打开文件,看到内容为SysWow64,如图所示:

System32中没有创建1.txt文件,32位程序访问System32中的1.txt文件,被自动映射到SysWow64文件夹中的1.txt文件,而如果用64位的Notepad编辑器打开System32中的1.txt文件,就会提示找不到文件:

由此就可以验证猜想“只要32位程序访问System32文件夹,无论是加载dll,还是读取文本信息,都会被映射到SysWow64文件夹”是正确的。
Program Files (x86)与Program Files
由System32与SysWow64的情况,考虑到Program Files (x86)与Program Files是不是也是这种情况。当32位程序访问Program Files目录时,会被自动映射到Program Files (x86)目录?
还是通过1.txt的方式来验证,发现当32位程序访问Program Files目录时,并没有被映射到Program Files (x86)目录。
32位程序真的需要访问System32吗
经过了这么多验证,总算是知道32位程序无法访问System32,只有64位程序才能访问,由此认为,这是Windows的一个非常大的坑。但是仔细想想,32位程序真的需要访问System32吗。就用这个dll检测工具来说吧。
如果在64位系统上,32位程序无法访问System32,为了访问它,就需要编译为64位。而如果程序编译为64位,就无法在32位系统上运行,同时由于在32位系统上不需要检测32位、64位,所以只需要32位程序即可。这可真是一个矛盾的事情,难道必须编译两个程序,一个32位,一个64位,来适应不同的操作系统吗。如果是C++的话,那么答案是这样的,必须编译一个32位一个64位。而DotNet就不一样了,编译的时候选择“AnyCPU”,并且不选择“首选32位”(VS2012中默认选中),编译后的程序,可以同时在32位和64位系统上运行,32位系统上是32位进程,64位系统上是64位进程,是不是很方便呢,这正是DotNet和AnyCPU的魅力所在。
至此,dll检测程序,不需要做任何代码修改,只需在编译的时候选择AnyCPU,并去掉“首选32位”选项,即可正常检测System32、SysWow64文件夹中的dll文件。
32位程序与64位程序的区别总结
至此,我想应该是真的明白了System32与SysWow64的区别了吧,这个不大不小的坑,算是迈过去了,那么就来总结一下32位程序与64位程序的区别:
- SysWow64文件夹,是64位Windows,用来存放32位Windows系统文件的地方,而System32文件夹,是用来存放64位程序文件的地方。
- .Net程序以AnyCPU配置,并选择“首选32位”编译,会以32位的进程运行,此时就无法访问System32文件夹中的文件;如果没有选择“首选32位”,则会以64位的进程运行,这样就可以访问System32文件夹了。(VS2012中,“首选32位”默认是选中的)。
- 32位程序的寻址空间有限,最多达到4G,而64位程序的寻址空间可以达到TB级,想要使用大内存的话,就升级到64位吧,好在DotNet程序从32位升级到64位比较简单,不像C++那么麻烦。
- 32位程序访问System32目录,会自动被映射到SysWOW64目录,而64位程序可以访问System32目录和SysWOW64目录。
- 32位程序与64位程序有各自的注册表。
- 32位与64位程序都可以访问Program Files (x86)与Program Files目录。
- 更多区别可以参考:32-bit and 64-bit Windows: frequently asked questions。
工作与学习过程中会遇到很多坑,一不小心就会跌倒,但是从哪里跌倒的就从哪里爬起来,总结经验教训,以饱满的热情再次起航,胜利就在不远的前方。
由于本人水平有限,文中如有不对之处,还请批评指正,本人不胜感激!
参考资料
dll文件32位64位检测工具以及Windows文件夹SysWow64的坑的更多相关文章
- dll文件32位64位检测工具以及Windows文件夹SysWow64的坑(很详细,还有自动动手编程探测dll)
阅读目录 dll文件不匹配导致数据库无法启动 究竟是System32还是SysWow64 区分dll文件32位64位的程序让我倍感迷惑 再次判断究竟是System32还是SysWow64——意想不到的 ...
- dll文件32位64位检测工具以及Windows文件夹SysWow64的坑【转发】
原文地址:http://www.cnblogs.com/hbccdf/archive/2014/03/09/3590916.html 自从操作系统升级到64位以后,就要不断的需要面对32位.64位的问 ...
- loadrunner动态从mysql取值 [需要下载跟数据库服务器一致的dll,32位或64位]
loadrunner中有参数化从数据库中取值,但是只是静态的,对于一些要实时取值的数据就game over了,比如取短信验证码,因为MySQL中有一个libmysql.dll,里面提供了可以操作数据库 ...
- 在代码生成工具Database2Sharp中使用ODP.NET(Oracle.ManagedDataAccess.dll)访问Oracle数据库,实现免安装Oracle客户端,兼容32位64位Oracle驱动
由于我们开发的辅助工具Database2Sharp需要支持多种数据库,虽然我们一般使用SQLServer来开发应用较多,但是Oracle等其他数据库也是常用的数据库之一,因此也是支持使用Oracle等 ...
- 错误: 未能完成程序集的安装(hr = 0x8007000b),.net程序关于使用Oracle.DataAccess.dll不同版本x86和x64问题,即oracle odp.net 32位/64位版本的问题
如果你的机器上安装了odp.net,且确信machine.config也有类似以下结节:(64位+.net 4.0环境下,machine.config可能会有4份,分别对应于.net2.0/4.0的3 ...
- [转载]使用32位64位交叉编码混淆来打败静态和动态分析工具 - wildsator
0×00 摘要 混淆是一种能增加二进制分析和逆向工程难度与成本的常用技术.主流的混淆技术都是着眼于使用与目标CPU相同的机器代码,在相同的处理器模式下,隐藏代码并进行控制.本文中引入了一种新的混淆方法 ...
- 【工具】清理Windows Installer冗余文件(支持64位NT6.x系统)
样子: 支持系统: Windows NT 5.x/6.x 32及64位所有系统.需.net framework 2.0运行环境 功能: 清理上述系统中冗余的Windows Installer补丁文件. ...
- Win7 下用 VS2015 编译最新 openssl(1.0.2j)包含32、64位debug和release版本的dll、lib(8个版本)
Win7 64位系统下通过VS2015编译好的最新的OpenSSL(1.0.2j)所有八个版本的链接库, 包含以下八个版本: 1.32位.debug版LIB: 2.32位.release版LIB: 3 ...
- 在Visual Studio项目中根据系统平台自动引用32或64位的DLL
最近在使用Noesis.Javascript.dll,但是这个DLL是有X86与X64二种版本的,我自己的电脑是64位的,但是别人的电脑是32位的.所以在别人那里使用的时候出了问题. 在VS里怎么引用 ...
随机推荐
- Linux 添加新磁盘,在线扩充空间
CentOS 7开发环境中的home 目录空间满了,需要增加空间 到虚拟机上执行"ls /sys/class/scsi_host",然后重新扫描SCSI总线来添加设备.如右图.然后 ...
- OpenGL超级宝典笔记----框架搭建
自从工作后,总是或多或少的会接触到客户端3d图形渲染,正好自己对于3d图形的渲染也很感兴趣,所以最近打算从学习OpenGL的图形API出发,进而了解3d图形的渲染技术.到网上查了一些资料,OpenGL ...
- H5项目开发分享——用Canvas合成文字
以前曾用Canvas合成.裁剪.图片等<用H5中的Canvas等技术制作海报>.这次用Canvas来画文字. 下图中"老王考到驾照后"这几个字是画在Canvas上的,与 ...
- 【开源毕设】一款精美的家校互动APP分享——爱吖校推 [你关注的,我们才推](持续开源更新3)附高效动态压缩Bitmap
一.写在前面 爱吖校推如同它的名字一样,是一款校园类信息推送交流平台,这么多的家校互动类软件,你选择了我,这是我的幸运.从第一次在博客园上写博客到现在,我一次一次地提高博文的质量和代码的可读性,都是为 ...
- CSS知识总结(九)
CSS常用样式 10.自定义动画 1)关键帧(keyframes) 被称为关键帧,其类似于Flash中的关键帧. 在CSS3中其主要以“@keyframes”开头,后面紧跟着是动画名称加上一对花括号“ ...
- 文档对象模型DOM通俗讲解
转自:http://www.jb51.net/article/42671.htm 在开始之前先说一点,DOM是非常容易理解的,但是大家说的太官方,让人很是难于理解,我们就用非常简单的语言翻译一遍.加深 ...
- postgresql 基本语法
postgresql数据库创建/修改/删除等写入类代码语法总结: 1,创建库 2,创建/删除表 2.1 创建表 create table myTableName 2.2 如果表不存在则创建表 crea ...
- centos tomcat安装
官网http://tomcat.apache.org/download-70.cgi下载zip的文件unzip apache-tomcat-7.0.54.zipmv apache-tomcat-7.0 ...
- 使用nginx反向代理,一个80端口下,配置多个微信项目
我们要接入微信公众号平台开发,需要填写服务器配置,然后依据接口文档才能实现业务逻辑.但是微信公众号接口只支持80接口(80端口).我们因业务需求需要在一个公众号域名下面,发布两个需要微信授权的项目,怎 ...
- 分享一个MySQL分库分表备份脚本(原)
分享一个MySQL分库备份脚本(原) 开发思路: 1.路径:规定备份到什么位置,把路径(先判断是否存在,不存在创建一个目录)先定义好,我的路径:/mysql/backup,每个备份用压缩提升效率,带上 ...