如何使用编程方式获取DLL依赖项

21

如何获取给定 DLL 或 EXE 文件的所有 DLL 依赖项列表?

换句话说,我想以编程方式执行与“Dependency Walker”工具相同的操作。

有没有 Windows(最好是.NET)API 可以实现这一点?

6个回答

13
您可以使用 EnumProcessModules 函数。像kaanbardak建议的托管API无法提供本机模块的列表。
例如,参见MSDN上的此页面
如果您需要静态分析dll,您必须深入了解PE格式并学习有关导入表的知识。请参见此优秀的教程以获取详细信息。

如果你想分析一个dll文件而不加载它,那么你需要读取它的导入表。 - aku
1
教程链接损坏(?) - Martin Ba
2
快速链接到Wayback Machine中优秀教程的最后一篇文章:https://web.archive.org/web/20120704231053/http://win32assembly.online.fr/pe-tut6.html - vvnurmi

4

注意:根据下面帖子的评论,我认为这个方法也可能会漏掉未管理的依赖项,因为它依赖于反射。

这是一段由Jon Skeet在bytes.com上编写的小型C#程序,用于.NET依赖关系检查器

using System;
using System.Reflection;
using System.Collections;

public class DependencyReporter
{
    static void Main(string[] args)
    {
        //change this line if you only need to run the code one:
        string dllToCheck = @"";

        try
        {
            if (args.Length == 0)
            {
                if (!String.IsNullOrEmpty(dllToCheck))
                {
                    args = new string[] { dllToCheck };
                }
                else
                {
                    Console.WriteLine
                        ("Usage: DependencyReporter <assembly1> [assembly2 ...]");
                }
            }

            Hashtable alreadyLoaded = new Hashtable();
            foreach (string name in args)
            {
                Assembly assm = Assembly.LoadFrom(name);
                DumpAssembly(assm, alreadyLoaded, 0);
            }
        }
        catch (Exception e)
        {
            DumpError(e);
        }

        Console.WriteLine("\nPress any key to continue...");
        Console.ReadKey();
    }

    static void DumpAssembly(Assembly assm, Hashtable alreadyLoaded, int indent)
    {
        Console.Write(new String(' ', indent));
        AssemblyName fqn = assm.GetName();
        if (alreadyLoaded.Contains(fqn.FullName))
        {
            Console.WriteLine("[{0}:{1}]", fqn.Name, fqn.Version);
            return;
        }
        alreadyLoaded[fqn.FullName] = fqn.FullName;
        Console.WriteLine(fqn.Name + ":" + fqn.Version);

        foreach (AssemblyName name in assm.GetReferencedAssemblies())
        {
            try
            {
                Assembly referenced = Assembly.Load(name);
                DumpAssembly(referenced, alreadyLoaded, indent + 2);
            }
            catch (Exception e)
            {
                DumpError(e);
            }
        }
    }

    static void DumpError(Exception e)
    {
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine("Error: {0}", e.Message);
        Console.WriteLine();
        Console.ResetColor();
    }
}

2
这段代码不会显示未管理的模块。 对于托管代码,Mono.Cecil将是一个更好的解决方案,因为它不需要您将程序集加载到AppDomain中(并且您以后也无法卸载程序集)。 - aku
添加额外的try/catch,如果一个程序集失败,则应列出其他程序集。 添加异常颜色和换行符,以便更容易阅读。 添加字符串字段,当您只想运行F5进行快速概述时(比将其添加到生成设置中更容易)。 - Kees C. Bakker

1

虽然这个问题已经有了一个被接受的答案,但是其他答案中引用的文档,如果没有损坏,就是旧的。与其阅读所有内容,只发现它不涵盖Win32和x64之间的差异或其他差异,我的方法是这样的:

C:\UnxUtils\usr\local\wbin>strings.exe E:\the-directory-I-wanted-the-info-from\*.dll > E:\TEMP\dll_strings.txt

这使我可以使用Notepad ++或gvim或其他工具搜索仍依赖于以120.dll结尾的MS dll的dll,以便找到需要更新的dll。您可以轻松地在您喜欢的编程语言中对此进行脚本化处理。鉴于我的搜索是针对VS 2015而进行的,并且这个问题是谷歌搜索的首要结果,我提供这个答案,以便可能有人寻找相同信息时能够得到帮助。

1

0
要读取正在运行的exe加载的DLL(模块),请使用ToolHelp32函数MSDN上的Tool Help文档

不确定它对于运行中的.Net exe会显示什么(我从未尝试过)。但是,它确实显示了从哪里加载了DLL的完整路径。通常,这是我在解决DLL问题时所需的信息。据说.Net已经删除了使用这些函数的必要性(查找DLL Hell以获取更多信息)。


0

如果您不想在程序中加载程序集,可以使用 DnSpy(https://www.nuget.org/packages/dnSpyLibs):

var assemblyDef = dnlib.DotNet.AssemblyDef.Load("yourDllName.dll");
var dependencies = assemblyDef.ManifestModule.GetAssemblyRefs();

请注意,在“ManifestModule”属性中,您可以获得所有所需的信息。

这是一个很好的开始...现在只需要弄清楚如何遍历整个树(即获取依赖项的实际汇编文件以加载它... - David V. Corbin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接