开发者社区> nothingfinal> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

dll文件32位64位检测工具以及Windows文件夹SysWow64的坑

简介: 自从操作系统升级到64位以后,就要不断的需要面对32位、64位的问题。相信有很多人并不是很清楚32位程序与64位程序的区别,以及Program Files (x86),Program Files的区别。
+关注继续查看

自从操作系统升级到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位PE文件

有了这些,我们就可以通过程序来判断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。检测结果如下图:

32位程序System32SysWow64检测结果对比

从图中看出,System32、SysWow64中检测出的所有的文件均为32位程序,根据常识也可以判断出,实际肯定不是这样的。一定是程序出了什么问题,那么直接用十六进制编辑器看一下两个文件是否一致吧。

再次判断究竟是System32还是SysWow64——意想不到的坑

通过UE查看两个文件夹中的msvcr110d.dll确实都是32位程序,而且用Beyond Compare进行比较,两个文件也没有差异。用工具查看两个文件的MD5也是完全一致:

System32_SysWow64_msvcr110d_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_SysWow64_文本文件

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

Notepad_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

工作与学习过程中会遇到很多坑,一不小心就会跌倒,但是从哪里跌倒的就从哪里爬起来,总结经验教训,以饱满的热情再次起航,胜利就在不远的前方。

由于本人水平有限,文中如有不对之处,还请批评指正,本人不胜感激!

参考资料

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
HBase2.0官方文档翻译-RegionServer Sizing Rules of Thumb
37.1. 热点(Hotspotting) Rows in HBase are sorted lexicographically by row key. This design optimizes for scans, allowing you to store related rows, or rows that will be read together, near each other.
855 0
windows下的快速搜索工具EveryThing小工具推荐
是不是经常有这样的经历:我刚刚创建了一个文件,但是突然就记不起保存的完整名字是啥了?或者前几天记得创建了某个文件,但是只知道叫什么名字,具体放在哪个盘,忘记了。
755 0
Read the article "WindowsNT Buffer Overflow's From Start to Finish"
WindowsNT Buffer Overflow's From Start to Finish I've read most of the articles on BO's(Buffer Overflows) on the net.
668 0
+关注
nothingfinal
软件开发,安全加密
文章
问答
文章排行榜
最热
最新
相关电子书
更多
ECS运维指南之Windows系统诊断
立即下载
《云服务器运维之Windows篇》
立即下载
FIS Global Accelerating Digital Intelligence in FinTech
立即下载