调用Module32First返回错误码87(参数无效)的JNA进程模块枚举问题
问题排查与修复方案
核心错误原因梳理
- MODULEENTRY32结构字段定义错误:Windows原生
MODULEENTRY32结构中,szModule和szExePath是固定长度的字符数组,你直接定义为无长度限制的String类型,会导致JNA计算结构大小偏差,dwSize赋值错误,触发参数无效报错。 - API参数传递方式错误:JNA对接原生结构体参数时,应直接传递
Structure实例而非手动获取的Pointer,手动调用write()再传指针的方式会跳过JNA的结构体对齐、内存自动同步逻辑,导致参数异常。 - dwSize赋值时机错误:你在字段声明时直接赋值
public int dwSize = this.size();,此时Structure尚未完成初始化,size()返回结果不准确。 - 快照句柄判断逻辑错误:
CreateToolhelp32Snapshot调用失败返回的是INVALID_HANDLE_VALUE(值为-1),不是null,你的判断逻辑会遗漏快照创建失败的场景。 - 可选兼容问题:如果你的程序是64位进程,需要枚举32位目标进程的模块,需要将
CreateToolhelp32Snapshot的flag改为TH32CS_SNAPMODULE | TH32CS_SNAPMODULE32(TH32CS_SNAPMODULE32值为0x00000010),否则也会枚举失败。
修复后的代码示例
1. 修正MODULEENTRY32结构定义
public static class MODULEENTRY32 extends Structure { public int dwSize; public int th32ModuleID; public int th32ProcessID; public int GlblcntUsage; public int ProccntUsage; public Pointer modBaseAddr; public int modBaseSize; public Pointer hModule; public char[] szModule = new char[256]; // 对应原生256长度的TCHAR数组 public char[] szExePath = new char[260]; // 对应原生MAX_PATH长度的TCHAR数组 public MODULEENTRY32() { super(); dwSize = size(); // 结构初始化完成后再赋值大小,保证数值正确 } public MODULEENTRY32(Pointer p) { super(p); read(); // 传入指针时自动读取内存数据 } // 提供便捷方法转换为Java字符串 public String getSzModule() { return Native.toString(szModule); } public String getSzExePath() { return Native.toString(szExePath); } @Override protected List<String> getFieldOrder() { return Arrays.asList("dwSize", "th32ModuleID", "th32ProcessID", "GlblcntUsage", "ProccntUsage", "modBaseAddr", "modBaseSize", "hModule", "szModule", "szExePath"); } }
2. 修正Kernel32接口定义
Pointer CreateToolhelp32Snapshot(int dwFlags, int th32ProcessID); // 第二个参数直接传MODULEENTRY32结构实例,无需手动处理指针 boolean Module32First(Pointer hSnapshot, MODULEENTRY32 lpme); boolean Module32Next(Pointer hSnapshot, MODULEENTRY32 lpme);
3. 修正业务调用代码
private void getModule() { // 如需兼容枚举32位进程模块,flag替换为 0x00000008 | 0x00000010 = 0x00000018 Pointer hSnapshot = this.kernel32.CreateToolhelp32Snapshot(WinAPI.TH32CS_SNAPMODULE, this.processId); // 正确判断快照句柄有效性 if(WinBase.INVALID_HANDLE_VALUE.equals(hSnapshot)) { System.out.println("创建快照失败,错误码:" + kernel32.GetLastError()); return; } try { WinAPI.MODULEENTRY32 moduleEntry32 = new WinAPI.MODULEENTRY32(); if(this.kernel32.Module32First(hSnapshot, moduleEntry32)) { do { // 模块处理逻辑 System.out.println("模块名:" + moduleEntry32.getSzModule() + ",路径:" + moduleEntry32.getSzExePath()); } while (this.kernel32.Module32Next(hSnapshot, moduleEntry32)); } else { System.out.println("枚举模块失败,错误码:" + kernel32.GetLastError()); } } finally { this.kernel32.CloseHandle(hSnapshot); } }
内容的提问来源于stack exchange,提问作者ThisIsntSparta
相关产品推荐
相关产品推荐

