缘起
最近,经常需要调查 dll
加载失败的问题。前一阵子刚分享了一篇,感兴趣的小伙伴而可以点击 这里。相信,有 windows
开发经验的小伙伴儿一定听过 Dependency Walker
这款工具,它可是查看模块依赖关系的神兵利器。但是在我的机器上,Dependency Walker
运行的特别慢,经常无响应!慢了这么久,是时候提提速了。
重现问题
很容易就可以重现问题,随便拖动一个 dll
文件(或者 exe
文件)到 Dependency Walker
中,等大概 5
秒钟,就会出现大名鼎鼎的无响应界面,需要等待很久才能等到分析结果。
探查敌情
在大动干戈之前,先用 process explorer
粗略查看 Dependency walker
中各个线程的运行情况。
看来,只有一个线程在工作(其它两个线程是系统工作线程),也是界面线程。如果界面线程都用来处理其它工作,长时间没有处理界面消息的话,界面就会无响应。
从调用栈可知,Dependency Walker
在运行过程中会不断的调用 SearchPath
和 FindFirstFile
。从这两个函数的名字可以猜测是在搜索文件。
如果有超多文件需要搜素,那么整个过程慢是合理的。只从 process explorer
中并不能知道 Dependency Waler
在搜索哪些文件,在哪些地方搜索。
小贴士:使用
process explorer
的线程功能,可以很方便的快速定位一些问题,如有必要,再上调试器进行分析。
process monitor
观察进程文件操作记录对于 process monitor
来说简直是小菜一碟。因为预计时间会比较长,而且只需要关心 Dependency Walker
相关的事件,所以在开始前,先做好过滤。
拖动 process monitor
上的靶子图标到 Dependency Walker
上,表示只关心当前这个 Dependency Walker
实例相关的事件。勾选 Drop filtered Event
表示丢弃过滤的事件,因为会过滤掉大部分无关的事件,生成的记录文件会比较小,可以记录更多有用的事件。
等 Dependency Walker
完成分析后,停止记录,查看文件相关事件。 dll
相关的事件一共有 247688
条。
好奇我是怎么知道的?请看下图:
观察一个比较典型的文件( api-ms-win-core-rtlsupport-l1-1-0.dll
)的相关记录。该文件相关的事件数量是 908
条。
该文件相关的部分事件截图如下:
在上图中,可以很明显的看到 Result
列大部分结果不是 Success
。这个在意料之中,因为正常情况下这个 dll
应该只在特定路径下才有。从 Path
列中的记录可知,搜索路径有很多个。
深入观察
如果仔细观察,可以发现搜索路径非常像典型的 Dll
搜索路径。Dll
搜索路径的官方参考资料链接为 https://docs.microsoft.com/zh-cn/windows/win32/dlls/dynamic-link-library-search-order?redirectedfrom=MSDN。
为了方便大家查看,我截取了相关部分,如下图:
我机器上的 PATH
环境变量如下图:
如此看来,Dependency Walker
正是按照典型的 Dll
搜索路径在查找啊(默认情况,可以修改)!
通过上面的简单分析,可以初步判定:除了把应该写在工作线程的代码写在 UI
线程有点问题,其它地方没有明显问题,确实有很多工作需要处理。
只能从减少搜索路径的方向着手了。如果可以少搜索一些地方,那么工作量会显著下降,速度相应的会提高上来了。我第一个想到的办法就是改变 PATH
环境变量的值。
手动设置 PATH
最简单的办法是,设置 PATH
环境变量的值为空,这样就可以避免搜索 PATH
环境变量指定的相关路径了。如果调整全局的 PATH
环境变量会影响所有程序,不可取。但是可以通过脚本启动 Dependency Walker
,在启动 Dependency Walker
之前,设置 PATH
为空,这样只会影响 Dependency Walker
。保存下面的批处理脚本为 start_depends.bat
,双击运行即可。
set PATH=""
"C:\My\tools\DEPENDS\x64\Depends.exe"
通过以上脚本启动的 Dependency Walker
眼中的 PATH
是空。
如果再次拖动相同的文件到 Dependency Walker
中,基本上是秒出结果。如下图:
在修改 PATH
环境变量之前,大概需要 2
分钟左右才能看到结果,而现在只需要用 1
秒左右,是不是不止快了 100
倍呢?对于一些依赖更加复杂的程序,效率提升的会更加明显!
小贴士:只有确定不会从
PATH
中加载对应的DLL
,才可以设置PATH
为空。既然可以设置PATH
为空,就可以设置PATH
为任意值。
但是,这样的操作终究有些不自然。难道,Dependency Walker
本身就不支持设置搜索路径吗?
Dependency Walker 设置
简单的浏览了一遍 Dependency Walker
的菜单,发现可以通过 Options -> Configure Module Search Order...
来设置模块搜索顺序及搜索路径。
设置界面如下图:
可以根据自己的需要进行设置。
小贴士:
The system's ’PATH‘ environment variable directories
指的是系统PATH
环境变量对应的路径,排除它,效果与把PATH
设置成空一样。
总结
Dependency Walker
可以自定义搜索目录,合理设置搜索目录,会大大提高搜索速度!默认情况下,
Dependency Walker
会搜索PATH
指定的路径,所以也可以通过修改PATH
环境变量的值达到相同的效果。如果长时间(大概
5
秒钟)没有处理界面消息的话,界面就会无响应。