目录

第1章说明    2

1.1 查找顺序    2

1.1.1 检查DllCharacteristics字段    3

1.1.2 读取manifset资源    3

1.1.3 读取manifset文件    4

1.1.4 载入    5

1.1.5 私有程序集    5

1.1.6 常规DLL的查找顺序    7

1.2 禁用隔离    7

1.2.1 修改DllCharacteristics字段    7

1.2.2 修改manifset    9

1.2.3 修改msvcr*.dll    11

第1章说明

1.1 查找顺序

使用VC++2008创建一个Win32项目,如下图所示

图1.1

编译这个程序(Debug版),将得到 Test.exe 文件。使用 eXeScope 打开Test.exe,可以发现 Test.exe 在运行时需要文件 msvcr90d.dll。如下图所示:

图1.2

运行Test.exe时,Windows需要找到msvcr90d.dll,并将其加载至内存,然后Test.exe才能正常运行。下面对msvcr90d.dll的查找顺序进行简要说明。

注意:如果操作系统比 Windows XP 还陈旧,msvcr90d.dll的查找顺序请参考1.1.6节。

1.1.1 检查DllCharacteristics字段

Windows首先检查Test.exe中DllCharacteristics字段的数值

可使用eXeScope查看DllCharacteristics字段的数值,如下图所示:

图1.3

根据上图可知:DllCharacteristics = 0x8140,因此:

(0x8140 & IMAGE_DLLCHARACTERISTICS_NO_ISOLATION) == 0

上面的宏 IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 其实就是0x200,它表示一个二进制位。这个二进制位为0/1表示使用/不使用隔离(isolation)。

Test.exe中DllCharacteristics的这个二进制位为零,表示使用隔离。如果某个程序不使用隔离,请跳至1.1.6节。

1.1.2 读取manifset资源

Test.exe使用隔离,所以Windows会检查Test.exe里有没有manifset资源(类型为24,编号为1)。下图表示Test.exe里存在manifset资源。

图1.4

可使用记事本打开文件Test.exe.embed.manifest,查看manifset资源的内容。如下图所示:

图1.5

1.1.3 读取manifset文件

如果上一步未找到manifset资源,Windows会检查是否存在manifset文件。

Test.exe的manifset文件就是Test.exe.manifest,它与Test.exe应在同一目录内,否则Windows无法找到。

Test.exe.manifest的内容应该与Test.exe.embed.manifest的内容保持一致。这样Windows才能顺利读取manifset信息。

1.1.4 载入

经过上面两步,Windows读取了manifset信息后发现:Test.exe需要程序集"Microsoft.VC90.DebugCRT"(参考上图)。

现在,Windows会到C:\WINDOWS\WinSxS目录下查找这个程序集。

在Windows7的C:\Windows\winsxs\Manifests目录下存在文件x86_policy.9.0.microsoft.vc90.debugcrt_1fc8b3b9a1e18e3b_9.0.30729.1_none_61305e07e4f1bc01.manifest,其内容如下图所示:

图1.6

其重点为:9.0.21022.8版本的程序集"Microsoft.VC90.DebugCRT"被重定向至版本9.0.30729.1。Windows会加载目录C:\Windows\winsxs\x86_microsoft.vc90.debugcrt_1fc8b3b9a1e18e3b_9.0.30729.1_none_bb1f6aa1308c35eb目录下的msvcr90d.dll。

注意:在Windows XP下,重定向文件为9.0.30729.1.policy,它位于C:\WINDOWS\WinSxS\Policies\x86_policy.9.0.Microsoft.VC90.DebugCRT_1fc8b3b9a1e18e3b_x-ww_037be232

1.1.5 私有程序集

如果Windows在C:\WINDOWS\WinSxS里找不到合适版本的msvcr90d.dll,就会在Test.exe所在目录进行查找。

位于C:\WINDOWS\WinSxS目录下的程序集属于共享程序集,能够被多个程序所共享。位于Test.exe所在目录的程序集属于Test.exe的私有程序集。

私有程序集的搜索步骤大致为:在Test.exe所在目录查找子目录Microsoft.VC90.DebugCRT,如果存在就会核对该目录内的文件Microsoft.VC90.DebugCRT.manifest。如果与要求的msvcr90d.dll版本一致,就会载入该目录下的msvcr90d.dll。使用私有程序集的具体操作如下:

将目录C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\redist\Debug_NonRedist\x86\Microsoft.VC90.DebugCRT复制到Test.exe所在目录(接下来用<exePath>表示)。记事本打开<exePath>\Microsoft.VC90.DebugCRT\Microsoft.VC90.DebugCRT.manifest,将版本号修改为Test.exe需要的版本号,亦即下图的版本号需要与图1.5的版本号保持一致。

图1.7

如果Test.exe需要载入Func.dll,而后者也需要程序集"Microsoft.VC90.DebugCRT",就可以把目录C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\redist\Debug_NonRedist\x86\Microsoft.VC90.DebugCRT复制到Func.dll所在目录(注意:不是Test.exe所在的目录)。如果Test.exe与Func.dll在同一目录内,而它们需要的程序集版本还不相同,那就比较麻烦了。

说明:

1、私有程序集的搜索步骤远比本文描述的复杂,详细信息请参考MSDN中的《Assembly Searching Sequence》,网址如下:

https://msdn.microsoft.com/en-us/library/windows/desktop/aa374224(v=vs.85).aspx

2、如果Windows在共享程序集和私有程序集里均未找到合适版本的msvcr90d.dll,就会终止程序并显示如下界面,即使msvcr90d.dll与Test.exe在同一目录下,Windows也不会载入。

图1.8

1.1.6 常规DLL的查找顺序

根据图1.2可知,Test.exe运行时还需要USER32.dll、KERNEL32.dll。这两个动态库不属于任何程序集,是常规的DLL文件。加载它们时的查找顺序请参考MSDN中的《Dynamic-Link Library Search Order》,网址如下:

https://msdn.microsoft.com/en-us/library/windows/desktop/ms682586(v=vs.85).aspx

1.2 禁用隔离

根据上文描述可知:C:\WINDOWS\WinSxS目录下允许存放某个组件(如:msvcr90d.dll)的多个版本,此即为"并列共享"(Side-by-side Assembly Sharing)。它主要是为了解决"DLL地狱"问题的,但它带来的问题比解决的问题并不少:VC++2005/2008编译生成的程序在用户的电脑上可能无法运行。

为了能够更方便的发布程序,接下来介绍几种禁用隔离的方法。

1.2.1 修改DllCharacteristics字段

修改DllCharacteristics字段的思路就是让DllCharacteristics & 0x200不等于零。下面分两种情况进行说明:一是没有源代码,只有执行程序(如:Test.exe);二是有源代码。

没有源代码,只能修改Test.exe文件。根据图1.3可知:Test.exe的字段DllCharacteristics首地址为0x13E,数值为0x8140。将0x8140改成(0x8140 | 0x200)=0x8340即可。

如下图所示,使用vc2008 的Open With 打开文件Test.exe

图1.9

选择二进制编辑器(Binary Editor)。

图1.10

按下Ctrl+G,输入地址0x13E,单击"OK"按钮,即可定位到0x13E处。当前的数值为0x8140(注意:低位在前,高位在后,就是下图中的40 81),将40 81改为40 83(即更改数值为0x8340),然后保存Test.exe。

图1.11

如果有源代码,只要修改一下配置重新编译一遍即可。具体就是修改"Allow Isolation"为"Don't allow side-by-side isolation",如下图所示:

图1.12

现在Test.exe运行时就不会被隔离了,不过此时运行它还是会出错。这是msvcr90d.dll的缘故,下文将说明如何对msvcr90d.dll进行修改。

1.2.2 修改manifset

只有Test.exe,没有源代码,且manifset在Test.exe.manifest文件时。把 <dependency>...</dependency>删除即可,如下图所示:

图1.13

只有Test.exe,没有源代码,且manifset在Test.exe内部时。用图1.10中的资源编辑器(Resource Editor)打开Test.exe,删除<dependency>...</dependency>之间的内容(或者直接删除整个manifest)。如下图所示。

图1.14

如果有源代码,只要修改一下配置重新编译一遍即可。具体就是修改"Generate Manifest"为"No",如下图所示:

图1.15

没有manifest指定程序集,Test.exe运行时就不会查找、加载程序集了,不过此时运行它还是会出错。这是msvcr90d.dll的缘故,下文将说明如何对msvcr90d.dll进行修改。

1.2.3 修改msvcr*.dll

使用VC++2005或VC++2008编译生成的程序,只要用到了msvcr80.dll、msvcr80d.dll、msvcr90.dll、msvcr90d.dll这四个文件中的任何一个,就必须启用隔离。一旦隔离被禁用,运行时就会崩溃并出现如下错误提示:

图1.16

这是msvcr??.dll造成的。以下代码节选自C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\crt\src\crtlib.c

#if defined (_CRT_CHECK_MANIFEST)

if (!_check_manifest(hDllHandle))

{

return FALSE;

}

#endif /* defined (_CRT_CHECK_MANIFEST) */

这段代码表示:加载msvcr??.dll时会调用_check_manifest,检查manifest信息。如果_check_manifest返回FALSE,整个程序就会崩溃。

现在再看看函数_check_manifest的代码(同样节选自crtlib.c):

static BOOL __cdecl _check_manifest(HMODULE hDllHandle)

{

pfnFindActCtxSectionStringW = (PFN_FINDAC) GetProcAddress(

hKernel32, "FindActCtxSectionStringW");

if (pfnFindActCtxSectionStringW == NULL)

{

return TRUE;

}

}

根据上面的代码可知:将字符串"FindActCtxSectionStringW"的首字符改成00H,则GetProcAddress就会返回NULL,_check_manifest就会返回TRUE。

因此,使用vc2008的二进制编辑器打开某个msvcr??.dll。找到FindActCtxSectionStringW后,将第一个字符F改成00H即可(也可全部改成 00H)。

图1.17

经过步骤1.2.1或1.2.2,以及步骤1.2.3,再将改好后的msvcr??.dll与Test.exe放在同一目录内,现在Test.exe即可载入同一目录下的msvcr??.dll了,再也不用到共享程序集、私有程序集里去查找、加载了。

Windows下DLL查找顺序的更多相关文章

  1. 【Windows下DLL查找顺序 】

    一.写作初衷 在Windows下单个DLL可能存在多个不同的版本,若不特别指定DLL的绝对路径或使用其他手段指定,在应用程序加载DLL时可能会查找到错误的版本,进而引出各种莫名其妙的问题.本文主要考虑 ...

  2. Python调用windows下DLL详解

    Python调用windows下DLL详解 - ctypes库的使用 2014年09月05日 16:05:44 阅读数:6942 在python中某些时候需要C做效率上的补充,在实际应用中,需要做部分 ...

  3. Windows下node-gyp查找VS安装路径简单解析

    node-gyp的作用我已经不想赘述了,这里给一个我之前文章的链接:cnblogs看这里,知乎看这里.本文主要从源码入手,介绍node-gyp查找VisualStudio的过程 为了方便我们研究nod ...

  4. Python调用windows下DLL详解 - ctypes库的使用

    在python中某些时候需要C做效率上的补充,在实际应用中,需要做部分数据的交互.使用python中的ctypes模块可以很方便的调用windows的dll(也包括linux下的so等文件),下面将详 ...

  5. python多继承下的查找顺序-MRO原则演变与C3算法

    在python历史版本中的演变史 python2.2之前: MRO原则: 只有经典类,遵循深度优先(从左到右)原则, 存在的问题:在有重叠的多继承中,违背重写可用原则 解决办法是再设计类的时候不要设计 ...

  6. vim7.4版本在windows下的配置文件及所在位置

    1.vim在windows下默认首先会查找"_vimrc"文件,如果没有则会找".vimrc".造成这个原因是windows早期不支持以点开头的文件及目录.2. ...

  7. windows下rundll32介绍

    最近看书介绍rundll32可以加载dll文件并执行其中导出函数,在MSDN中我们可以看到绍http://support.microsoft.com/kb/164787/zh-cn rundll32调 ...

  8. Windows下静态库与动态库的创建与使用

    Windows下静态库与动态库的创建与使用 学习内容:本博客介绍了Windows下使用Visual C++ 6.0制作与使用静态库与动态库的方法. --------CONTENTS-------- 一 ...

  9. Linux的.a、.so和.o文件 windows下obj,lib,dll,exe的关系 动态库内存管理 动态链接库搜索顺序 符号解析和绑定 strlen函数的汇编实现分析

    Linux的.a..so和.o文件 - chlele0105的专栏 - CSDN博客 https://blog.csdn.net/chlele0105/article/details/23691147 ...

随机推荐

  1. SSH配置私钥登陆服务器

    前言 本文基于实际Linux管理工作,实例讲解工作中使用ssh证书登录的实际流程,讲解ssh证书登录的配置原理,基于配置原理,解决实际工作中,windows下使用SecureCRT证书登录的各种问题, ...

  2. iis 没目录文件

    方法一: <system.webServer> <directoryBrowse enabled="true"/> </system.webServe ...

  3. java中的构造函数

    在c++中就学习了构造函数,今天学习java又碰到了构造函数,重新写一篇博客来理解一下 其实直接听这个词并不能理解这是什么,但其实看了它的作用,就很好理解了 当创建一个对象时,往往需要做一些初始化工作 ...

  4. win7下如何安装JDK

    第一步:点击下载好的JDK安装程序,百度一下,网上很多,然后弹出如下界面,点击“下一步”即可. 2 第二步:此时比较关键,设置安装的路径,可以事先建立一个文件夹,以在D盘为例,如D:\java. 3 ...

  5. Python3基础 while配合random输出10个随机整数

    镇场诗:---大梦谁觉,水月中建博客.百千磨难,才知世事无常.---今持佛语,技术无量愿学.愿尽所学,铸一良心博客.------------------------------------------ ...

  6. 【leetcode❤python】 88. Merge Sorted Array

    #-*- coding: UTF-8 -*-class Solution(object):    def merge(self, nums1, m, nums2, n):        "& ...

  7. 【leetcode❤python】 223. Rectangle Area

    #-*- coding: UTF-8 -*-#先判断是否有重叠class Solution(object):    def computeArea(self, A, B, C, D, E, F, G, ...

  8. php中一个"异类"语法: $a && $b = $c;

    php中一个"异类"语法: $a && $b = $c;     $a = 1;$b = 2;$c = 3;$a && $b = $c;echo & ...

  9. LeetCode-Repeated DNA

    关于位操作符.如<<, value << num ,其中,num指定要位移值value移动的位数,每左移一个位,高阶位都被移出(直接丢掉),并用0填充右边.. 道理明明很简单啊 ...

  10. ,2,liunx命令格式

    一.命令基本格式 ~用户的初始登录位置   /root  这个叫root用户的家目录,每个用户都有自己的家 超级用户的家是根目录,普通用户的家是home下的二级目录   :/home/uer1 pwd ...