Dependency Walker使用说明(对比软件运行与跟踪情况)

本文详细介绍了Windows系统中的动态链接库(DLL)的基本概念、重要性及应用技巧,并通过实例演示了如何利用DependencyWalker等工具分析DLL文件。

在Windows世界中,有无数块活动的大陆,它们都有一个共同的名字——动态链接库。现在就让我们走进这些神奇的活动大陆,找出它们隐藏已久的秘密吧!

  初窥门径:Windows的基石

  随便打开一个系统目录,一眼望去就能看到很多扩展名DLL的文件,这些就是经常说的“动态链接库”,DLL是Dynamic Link Library(即“动态链接库”)的缩写。从Microsoft公司推出首个版本的Windows以来,动态链接库就一直是这个操作系统的基础。

  1.看看DLL里有什么

  与其用晦涩的专业术语来解决DLL是什么,不如先来看看DLL里有什么。DLL和EXE文件一样,其中包含的也是程序的二进制执行代码和程序所需的资源(比如图标、对话框、字符串等),可是为什么要把代码放在DLL里面,而不是做成EXE呢?其实DLL中的代码是以API函数形式出现的,通俗地说,DLL中包含的程序代码都被做成了一个个小模块,应用程序通过按下所需DLL中特定的按钮,来调用DLL中这个按钮所代表的功能。在使用“记事本”等程序时,如果要保存文件或打开文件,就会弹出通用文件对话框,让我们选择文件位置。你可知道,这就是调用了系统底层DLL中的通用对话框界面。

  2.系统中几个重要的DLL

  Windows中有3个非常重要的底层DLL:Kernel32.dll、User32.dll、GDI32.dll。其中Kernel32.dll顾名思义就是内核相关的功能,主要包含用于管理内存、进程和线程的函数;而User32.dll中包含的则是用于执行用户界面任务的函数,比如把用户的鼠标点击操作传递给窗口,以便窗口根据用户的点击来执行预定的事件;GDI32.dll的名称用了缩写,全称是Graphical Device Interface(图形设备接口),包含用于画图和显示文本的函数,比如要显示一个程序窗口,就调用了其中的函数来画这个窗口。

  3.为什么要用DLL

  刚才在谈到这个问题的时候,我们只是解释了DLL将程序代码封装成函数的原理。为什么封装成函数,就能成为系统中大量使用DLL的理由呢?

  ①扩展应用程序

  由于DLL能被应用程序动态载入内存。所以,应用程序可以在需要时才将DLL载入到内存中,这让程序的可维护性变得很高。比如QQ的视频功能需要升级,那么负责编写QQ的程序员不必将QQ所有代码都重写,只需将视频功能相关的DLL文件重写即可。

  ②便于程序员合作

  这个和我们最终用户关系不大,仅供了解。我们都知道编程工具有很多,比如VB、VC、Delphi等,如果好几个人合作来编写一个大的程序,那么可能有的人用VB,有的人用VC,每人负责的部分所使用的编程语言都不同,究竟放在哪个编译器中进行编译呢?这就好比一群来自各个国家的人在共同编写一篇文章,如果他们所使用的语言都不同,写出来的文章怎么可能凑到一起呢?而有了DLL后,可以让VC程序员写一个DLL,然后VB程序员在程序中调用,无需为怎么将它们都编译为一个单独的EXE而发愁了。

  ③节省内存

  如果多个应用程序调用的是同一个动态链接库,那么这个DLL文件不会被重复多次装入内存中,而是由这些应用程序共享同一个已载入内存的DLL。就好比一个办公室中,很少会为每一个员工配置一台饮水机的,而是在一个公共位置放上一个饮水机,所有需要喝水的职员都可以共用这台饮水机,降低了成本又节约了空间。

  ④共享程序资源

  包括刚才提到过的通用文件对话框在内,DLL文件提供了应用程序间共享资源的可能。资源可以是程序对话框、字符串、图标,或者声音文件等。

  ⑤解决应用程序本地化问题

  在下载了某个程序的汉化包后,打开汉化说明,经常可以看到用下载包中的DLL文件覆盖掉程序原来的DLL,汉化就完成了。这些程序都是将执行代码和应用程序界面分开编写了,所以汉化者只需简单地将其中和程序界面相关的DLL汉化并发布即可。

求知若渴:探究DLL的真相 
  谁知道DLL里究竟有多少函数,又有谁知道EXE调用了哪个DLL的哪些函数?其实,这个问题并不难解决。还记不记得本刊2004年第6期的《无间盗IV——盗亦有盗》中介绍的分析EXE文件的工具Dependency Walker(以下简称Depends)今天我们要用它当探险工具,把DLL真相探个通通透透。 
  1.看看DLL里有多少函数 
  第一步:下载并解压Depends,运行其中的depends.exe,然后选择菜单“File→Open”(文件→打开),在文件选择框中选中需要分析的DLL文件并打开,此处选择QQ目录下的QQZip.dll。 
  第二步:在程序左侧的树状栏中就列出了这个DLL使用了哪些其他DLL的功能函数(原来DLL中还可以调用其他DLL^O^),而右侧的两个分栏列表分别显示了函数输入及输出表,函数输出表即为该DLL提供给其他EXE或者DLL调用的函数的总列表。 
  第三步:函数输出表的Function栏中即为输出函数的名称(见图1),在QQZip.dll中共发现了2个函数:Unzip、Zip。因此可以判断该DLL在QQ程序中负责压缩和解压缩的任务。

  2.审审EXE究竟用了哪个DLL 
  还是拿QQ来作为例子,在Depends中打开QQ.exe,这时界面左侧的树状列表中显示的就是QQ.exe调用的DLL列表(见图2),如果展开这些DLL分支,还会发现其他的DLL,这就说明QQ调用的这些DLL文件还有可能(几乎是肯定)再调用别的DLL。这就好比买了一台新的DVD机,可能其中用的机芯是SONY的,而这个机芯里的一个小电容又有可能是别的公司的,这是同样的道理。

3.用DLL看穿EXE真面目 
  刚才得到了QQ.exe所使用的DLL列表,其实通过这个列表,还能分析出很多别的信息。比如其中包含MFC42.dll,所以可以判断QQ.exe是采用VC(即Visual C++)编写的,而包含WSOCK32.dll则说明这个程序带有网络通讯功能(废话!QQ如果不能网络通讯还有什么用……)。以下是一个简表,大家在分析别的EXE时可以根据其所使用的DLL来对其功能进行初步判断。 
  DLL文件名 可以判断出的EXE信息 
  MFC42.dll 使用VC5.0/6.0编写。 
  VBRun*.dll “*”代表数字版本号,使用VB3.0/4.0编写。 
  MSVBVM50.dll 使用VB5.0编写,在Windows 98(SE)上自带该DLL。 
  MSVBVM60.dll 使用VB6.0编写,在Windows Me/2000/XP等系统上自带该DLL。 
  ADVAPI32.dll 可能会进行注册表操作。 
  WSOCK32.dll 具备网络通讯功能。 
  WS2_32.dll 具备网络通讯功能。 
  WININET.dll 具备HTTP浏览、下载等功能,典型的例子是浏览器、下载工具。 
  WINMM.dll 具备多媒体播放能力。 
  DDRAW.dll 游戏、高级图像处理工具。 
  D3D*.dll 3D游戏,或者动画处理工具。 
  4.DLL是个大宝库 
  除供应用程序调用函数的DLL外,还有另一种用来保存资源的DLL,比如QQ目录下的QQRes.dll,用Depends打开后发现没有任何输出函数,难道是一个鸡肋DLL?可是改用资源工具Resource Hacker(下载地址:http://www.mydown.com/soft/42/42058.html)打开这个DLL后,就发现原来其中保存了这么多QQ的资源,包括图标、音乐、图片、字符串、对话框……(见图3)

刨根问底:DLL的寓言 
  DLL引起的故障是很常见的,为什么会引起故障?遇到故障怎么解决?嘘~偷听一下DLL的对话,你就会明白了。 
  1.从搬运工谈接口兼容性 
  在Windows工地上,有一个名叫EXE的包工头,他手下有很多称为DLL的建筑工人。其中有一个专门负责搬运的DLL(暂且称为“搬运工A”),每次需要搬运水泥时,包工头EXE都只要对他喊一声:“来!搬。” 
  过了一段时间,搬运工A觉得自己的效率太低,于是从原来的每次搬1袋水泥改成了每次搬3袋水泥。改进了搬运方法后,EXE包工头仍然每次只是喊一声:“来!搬。”却不知搬运工A已经改变了搬运的方法。 
  但又过了一段时间,包工头EXE把搬运工A给辞退了,从别的工地上找来了另一个DLL(暂且称为“搬运工B”)。这个搬运工在别的工地的时候,搬运东西特别快,所以包工头EXE决定把搬运工作给“升级”一下。但真正开始工作时,包工头才发现出了问题……现在不管叫几遍“来!搬。”这个新来的搬运工B都不知道究竟应该搬什么。 
  上面的例子中,搬运工A改进搬运方法,但EXE调用它的方法仍不变,这就是DLL升级的原理,改进了内部的实现方法,但调用接口不变,这样EXE文件不用跟着升级,就能调用新版本的DLL了。而搬运工B的故事告诉我们,不管新版本的DLL效率多高,如果接口(可以理解为DLL中输出的函数名)与原来的不一致,那么EXE就不知道也无法调用它了。 
  2.登记身份证的DLL 
  在系统故障中,有很多都是由于DLL文件没有注册造成的,比如Windows XP的压缩文件夹功能出现故障就很有可能是系统目录中的zipfldr.dll没有注册造成的,这类故障的解决方法也大多是运行如下命令: 
  regsvr32 DLL文件名 
  很多人不理解为什么要这么做,是不是所有的DLL都能这样做呢? 
  其实系统中有两种DLL,一种是不需注册即可使用的,另一种则是必须经过系统登录(即注册)才能使用的。就好像一个临时工,和一个记录在员工名单上的长期合同工的区别一样。如何才能区分这两种DLL呢?方法很简单,用刚才的Depends打开这个DLL,同样是看函数输出表,如果其中包含以下两个函数(前者是注册DLL,后者是反注册DLL),那么就一定是需要注册才能使用的DLL了。 
  DllRegisterServer 
  DllUnregisterServer 
  而regsvr32这个命令,实际上就是调用DLL中的这两个函数(“regsvr32 /u DLL文件名”调用的即为DllUnregisterServer反注册函数)。 
  3.插件DLL的秘密 
  Winamp、Foobar 2000等很多软件都具有插件功能,从网上下载一个DLL放在插件目录下就能让程序支持新的功能,这是怎么做到的呢?就拿时下流行的播放软件“千千静听”来举例吧。 
  “千千静听”的插件目录在该软件安装目录下的Addin子目录下,程序的插件目录一般都会以“Plugins”、“Addin”来命名。在“千千静听”的插件目录中有许多DLL文件,比如tt_asf.dll、tt_rm.dll等,从文件名中就能看出这些DLL是用来让这个播放器支持各种不同类型的音频文件的。同样,用Depends打开这些文件,你就会发现这些文件的输出函数表中都包括一个同样的函数:ttpGetSoundAddIn(见图4)。

  这就是插件的秘密,各种支持插件功能的程序在发布时,都会同时发布一份插件协议,协议中规定了该程序将要调用的插件DLL中必须包含的函数名称及相关的参数规则,然后第三方的插件程序员在编写这个程序的插件时就根据这个插件的标准来编写DLL的输出函数。 
  ①对于插件tt_asf.dll 
  ttplayer.exe(“千千静听”主程序)对tt_asf.dll说:“我要调用你的ttpGetSoundAddIn函数!” 
  tt_asf.dll回答:“OK。” 
  ②如果把不相关的DLL放进AddIn目录 
  ttplayer.exe对未知DLL说:“我要调用你的ttpGetSoundAddIn函数!” 
  tt_asf.dll回答:“那是什么函数?从来没听说过!”

0
<think>好的,我现在需要帮助用户排查程序在本地正常运行但在客户电脑上无法运行的问题。用户之前提到了使用WMI方法关机失败的情况,现在他想知道为什么程序在本地可以运行而在客户电脑上不行,以及如何解决。 首先,我应该回顾用户之前的提问和我的回答。之前用户遇到的问题是使用C#中的WMI方法关机失败,我分析了权限不足、参数错误、异常处理、系统策略、WMI服务未运行和代码逻辑错误等可能原因,并给出了解决方案。现在用户的问题更广泛,涉及任何程序在本地正常但在客户电脑无法运行情况,需要综合考虑各种可能性。 接下来,用户提供的引用内容虽然当前问题不完全相关,但可能有帮助。引用1提到惠普电脑无法启动时的重置方法,引用2和3讨论了DLL缺失、配置错误和软件兼容性问题。这些信息提示我应该考虑系统环境差异、依赖项缺失、权限问题、配置错误等。 我需要根据这些线索,结合常见的软件部署问题,列出可能的原因和解决方案。可能的因素包括系统版本差异、缺少依赖库(如.NET框架、VC++运行库)、权限不足、路径或配置文件错误、安全软件拦截、硬件兼容性问题、用户账户控制设置、注册表问题、网络问题以及数据文件损坏等。 接下来,我应该按照逻辑顺序组织这些原因,并为每个原因提供具体的排查步骤和解决方案。例如,检查系统版本和架构,安装必要的运行库,以管理员身份运行程序,检查防火墙设置等。同时,需要结合用户之前提到的WMI问题,确保权限和服务配置正确。 此外,用户可能需要分步的排查南,所以应该建议从简单到复杂的步骤,比如先检查依赖项,再查看权限,最后处理兼容性设置。同时,提供工具推荐如Dependency Walker、Process Monitor等,帮助用户诊断问题。 最后,总结关键点,强调系统环境差异的重要性,并建议使用标准化部署工具减少环境问题。确保回答结构清晰,步骤明确,帮助用户逐步解决问题。</think>### 程序本地正常但在客户电脑无法运行的原因及解决方案 根据你描述的场景(结合之前的 WMI 关机问题及引用中的线索),程序在本地正常但在客户电脑无法运行的常见原因可分为 **系统环境差异**、**依赖项缺失**、**权限或策略限制** 和 **配置错误** 四大类。以下是详细排查步骤及解决方案: --- #### 一、**系统环境差异** 1. **操作系统版本或架构不兼容** - **现象**:程序在 Windows 10 开发环境正常,但在客户 Windows 7/8 或旧版本系统报错。 - **排查方法**: - 检查客户电脑的 Windows 版本(`Win + R` → `winver`)。 - 确认程序是否面向特定架构(x86/x64),例如:客户电脑为 32 位系统,但程序编译为 64 位。 - **解决方案**: - 开发时设置目标框架兼容旧版本(如 .NET Framework 4.0 而非 4.8)。 - 发布时生成 `AnyCPU` 或分发包(x86/x64)。 2. **未安装必要运行库** - **常见问题**: - .NET Framework 版本过低(如程序依赖 4.7.2,但客户电脑仅安装 4.5)。 - VC++ Redistributable 缺失(依赖 C++ 编写的组件)。 - **解决方案**: - 使用工具检测依赖项(如 [Dependency Walker](http://www.dependencywalker.com/))。 - 打包安装程序时包含运行库(如通过 Inno Setup 自动安装 VC++ Redistributable)。 --- #### 二、**依赖项缺失或路径问题** 1. **动态链接库(DLL)丢失** - **现象**:报错提示缺少 `.dll` 文件(如 `vcruntime140.dll`)[^2]。 - **原因**: - 第三方库未正确部署(如 NuGet 包未包含在发布文件中)。 - 程序引用了绝对路径的资源(如配置文件路径为 `C:\Program Files\...`)。 - **解决方案**: - 使用 `Process Monitor` 追踪程序加载的 DLL 路径。 - 确保所有依赖文件主程序在同一目录,或配置相对路径。 2. **配置文件或注册表项缺失** - **案例**:程序依赖注册表中的特定键(如许可证信息),但客户电脑未写入。 - **排查方法**: - 在开发环境使用 `RegShot` 对比程序运行前后的注册表变化。 - 检查配置文件(如 `appsettings.json`)是否存在且格式正确。 --- #### 三、**权限或安全策略限制** 1. **用户权限不足** - **现象**:程序需要管理员权限但未以管理员身份运行(如操作注册表或系统目录)。 - **解决方案**: - 在程序清单文件(`app.manifest`)中添加 `<requestedExecutionLevel level="requireAdministrator" />`。 - 提示客户右键程序 → **以管理员身份运行**。 2. **安全软件拦截** - **常见问题**:防火墙、杀毒软件或组策略阻止程序运行[^2]。 - **排查步骤**: - 临时关闭客户电脑的杀毒软件(如 Windows Defender、360 安全卫士)。 - 检查事件查看器(`eventvwr.msc`)中的安全日志是否有拦截记录。 3. **用户账户控制(UAC)限制** - **现象**:程序尝试写入 `Program Files` 目录但被 UAC 阻止。 - **解决方案**: - 将用户数据存储到 `AppData` 目录(`Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)`)。 --- #### 四、**硬件或驱动问题** 1. **硬件兼容性** - **案例**:程序依赖特定硬件(如 GPU 加速),但客户电脑显卡驱动未更新。 - **排查方法**: - 使用 `DirectX 诊断工具`(`dxdiag`)检查驱动状态。 - 捕获程序日志,确认是否抛出硬件相关异常(如 `OpenGL` 初始化失败)。 2. **多显示器或分辨率问题** - **现象**:界面布局错乱或窗口无法显示(常见于 WinForms/WPF 程序)。 - **解决方案**: - 测试不同分辨率和缩放比例(如 125% 缩放导致控件重叠)。 - 使用 `DPI_AWARENESS` 声明支持高 DPI。 --- #### 五、**网络或服务依赖** 1. **网络连接失败** - **案例**:程序依赖在线 API 或数据库,但客户网络受限。 - **排查步骤**: - 使用 `telnet <IP> <端口>` 测试网络连通性。 - 捕获 `WebException` 并记录详细错误信息。 2. **后台服务未运行** - **案例**:依赖的服务(如 SQL Server、IIS)未启动或被禁用。 - **解决方案**: - 在代码中增加服务状态检查(`ServiceController` 类)。 ```csharp using System.ServiceProcess; ServiceController sc = new ServiceController("MSSQLSERVER"); if (sc.Status != ServiceControllerStatus.Running) { Console.WriteLine("SQL Server 服务未运行!"); } ``` --- ### 快速排查流程 1. **收集错误信息**: - 截图错误弹窗,记录事件查看器日志。 - 使用 `try-catch` 捕获异常并写入日志文件。 2. **最小化复现**: - 在客户电脑运行一个仅包含核心功能的测试程序(排除其他模块干扰)。 3. **对比环境**: - 使用 `System.Environment` 类输出系统信息: ```csharp Console.WriteLine($"OS: {Environment.OSVersion}"); Console.WriteLine($"64-bit OS: {Environment.Is64BitOperatingSystem}"); Console.WriteLine(".NET Framework: " + Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full", "Release", "未安装")); ``` --- ### 工具推荐 | 工具 | 用途 | |---------------------|-----------------------------| | **Dependency Walker** | 分析缺失的 DLL 或依赖冲突 | | **Process Monitor** | 监控文件、注册表、网络访问行为 | | **WireShark** | 排查网络连接问题 | | **API Monitor** | 跟踪 Windows API 调用情况 | --- ### 总结 程序在客户电脑无法运行的根本原因通常是 **环境不一致性**。建议: - 使用容器化技术(如 Docker)统一运行环境。 - 提供清晰的运行环境说明(如 .NET 版本、VC++ 运行库链接)。 - 在代码中增加健壮的错误处理和日志记录(如 `NLog` 或 `Serilog`)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值