You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.17 12:36:01