Windows下获取当前进程所有DLL路径需用EnumProcessModules获取模块句柄,再通过GetModuleFileNameEx或GetMappedFileName转换为路径;前者需psapi.lib且Vista+稳定,后者返回设备路径需QueryDosDevice解析;EnumProcessModulesEx支持32/64位过滤,但均不直接返回路径;GetModuleInformation仅提供内存信息,与路径无关;跨平台不可行,必须限定Win32 API。

Windows平台用 EnumProcessModules 获取模块句柄再查路径
在 Windows 上,C++ 获取当前进程所有已加载 DLL 路径,核心是先枚举模块句柄,再对每个句柄调用 GetModuleFileNameEx 或 GetMappedFileName。注意:EnumProcessModules 返回的是模块基址(HMODULE),不是路径,必须额外转换。
常见错误是直接用 GetModuleFileName(nullptr, ...) —— 这只返回主模块(EXE)路径,漏掉所有 DLL;或者用 EnumProcessModules 但没正确处理缓冲区大小,导致只拿到前几个模块。
- 必须以
GetCurrentProcess()获取当前进程句柄,不能传INVALID_HANDLE_VALUE或其他进程句柄 - 第一次调用
EnumProcessModules时传nullptr作为lphModule参数,仅用于获取所需缓冲区大小(cb输出值) -
GetModuleFileNameEx需要psapi.lib链接,且 Vista+ 才保证稳定;若目标系统可能为 XP,改用GetMappedFileName(需kernel32.lib,但返回路径格式为\Device\HarddiskVolumeX\...,需用QueryDosDevice映射) - 模块数量动态变化,建议循环重试或分配足够大缓冲区(如 4096 个
HMODULE)
GetModuleInformation 和 EnumProcessModulesEx 的适用场景差异
GetModuleInformation 本身不返回路径,只提供模块大小、入口点等内存信息,常被误认为能取路径;而 EnumProcessModulesEx 是增强版枚举函数,支持按类型过滤(如只枚举 32/64 位模块、或只枚举已加载的 DLL,不含 EXE),但它仍不返回路径——仍需配合 GetModuleFileNameEx。
关键区别在于:如果进程混合加载了 Wow64 下的 32 位 DLL(在 64 位系统上运行 32 位程序),EnumProcessModules 可能漏掉部分模块,此时必须用 EnumProcessModulesEx 并传入 LIST_MODULES_32BIT 或 LIST_MODULES_64BIT 标志。
- 32 位程序在 64 位 Windows 上默认只能看到 32 位模块,
EnumProcessModulesEx+LIST_MODULES_ALL才可能覆盖全部(需管理员权限且系统支持) -
GetModuleInformation主要用于调试或内存分析场景,比如判断某模块是否被重定位、校验 PE 头偏移,和路径无关 - 不要依赖
GetModuleHandle枚举——它只返回已通过名字查到的模块,无法发现隐式加载或延迟加载的 DLL
避免 ERROR_PARTIAL_COPY 和空路径的典型处理
调用 GetModuleFileNameEx 时频繁遇到返回值为 0 且 GetLastError() 等于 ERROR_PARTIAL_COPY,本质是目标模块处于卸载中或内存页被换出,但更常见原因是:当前进程没有足够的权限读取该模块信息(尤其系统 DLL 或受保护进程加载的模块)。
另一个高频问题是返回空字符串或只有文件名(如 kernel32.dll),这是因为某些模块由内存映射加载(如用 LoadLibraryEx + LOAD_LIBRARY_AS_DATAFILE),其路径未被系统记录,或模块来自资源段、压缩包内解压后加载,无磁盘路径。
- 对
GetModuleFileNameEx返回失败的模块,可降级尝试GetMappedFileName(对当前进程有效,无需额外权限) - 若
GetMappedFileName返回\Device\...路径,需解析符号链接:提取卷名(如\Device\HarddiskVolume2),再调用QueryDosDevice查到对应盘符(如C:) - 跳过返回长度 ≤ 1 的结果——这通常表示路径不可用,不是 bug,而是 Windows 加载机制本身的限制
- 不要对
ntdll.dll或kernelbase.dll强求完整路径,它们可能被映射到共享内存区域,路径字段为空是正常行为
跨平台不可行,别试图用 POSIX 方法绕过
Linux/macOS 没有“DLL 模块路径列表”这个概念:动态库通过 dlopen 加载,路径由 dlinfo 或 /proc/self/maps 推断,但无法可靠还原原始 so/dylib 文件路径(尤其 strip 后或从内存加载)。C++ 标准库也不提供跨平台模块枚举接口。
所以任何 “写一次,到处运行” 的封装都会在 Windows 外失效。硬套 std::filesystem 或 dl_iterate_phdr 只能得到部分映射区间,无法区分哪些是真正意义上的“加载的 DLL”,哪些只是内存段。
- Windows 方案必须限定为 Win32 API + psapi/k32,且明确标注仅适用于 MSVC/MinGW-w64 编译环境
- 如果项目需同时支持多平台,应在构建期用 CMake 判断平台,Windows 分支走
EnumProcessModules,其他平台只记录主模块路径或直接放弃 DLL 列表功能 - 第三方库如 Boost.DLL 只包装
LoadLibrary和符号查找,不提供枚举能力——它也不知道自己被谁加载
实际跑通的关键不在代码长短,而在接受 Windows 模块管理本身的不透明性:有些 DLL 就是没有路径,有些路径就是拿不到,这不是你代码的问题。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











