调用 Module32First 给出无效参数(错误代码 87)

Calling Module32First Gives Invalid Parameter (Error Code 87)

我正在使用 JNA 并编写一些代码来枚举进程中所有可用的模块。我通过 CreateToolhelp32Snapshot 成功获取了快照句柄,但是在我第一次调用 Module32First 时,我收到“无效参数”的错误代码 87。

下面是我正在使用的相关代码:

有问题的代码:

   private void getModule() {
        Pointer hSnapshot = this.kernel32.CreateToolhelp32Snapshot(WinAPI.TH32CS_SNAPMODULE, this.processId);

        if(hSnapshot != null) {
            WinAPI.MODULEENTRY32 moduleEntry32 = new WinAPI.MODULEENTRY32();

            moduleEntry32.write(); // Write the struct in memory (for dwSize to be registered)

            Pointer moduleEntry32Pointer = moduleEntry32.getPointer();

            if(this.kernel32.Module32First(hSnapshot, moduleEntry32Pointer)) {
                 // irrelevant stuff here
            }
        }

        System.out.println(this.kernel32.GetLastError()); // Prints out "87"

        this.kernel32.CloseHandle(hSnapshot);

    }

Kernel32 映射器Class:

    Pointer CreateToolhelp32Snapshot(int dwFlags, int th32ProcessID);

    boolean Module32First(Pointer hSnapshot, Pointer lpme);

    boolean Module32Next(Pointer hSnapshot, Pointer lpme);

ModuleEntry32 结构:

    public static class MODULEENTRY32 extends Structure {
        public int dwSize = this.size();
        public int th32ModuleID;
        public int th32ProcessID;
        public int GlblcntUsage;
        public int ProccntUsage;
        public Pointer modBaseAddr;
        public int modBaseSize;
        public Pointer hModule;
        public String szModule;
        public String szExePath;

        public MODULEENTRY32() {
            super();
        }

        public MODULEENTRY32(Pointer p) {
            super(p);
        }

        protected List<String> getFieldOrder() {
            return Arrays.asList(new String[] {"dwSize", "th32ModuleID", "th32ProcessID", "GlblcntUsage", "ProccntUsage", "modBaseAddr", "modBaseSize", "hModule", "szModule", "szExePath"});
        }
    }

最后,这是“WinAPI。TH32CS_SNAPMODULE”:

    public static final int TH32CS_SNAPMODULE = 0x00000008;

注意:我列举的进程已经用OpenProcess打开了,是有效的。 processId也是合法的,而且获取的也很正常

如有任何帮助,我们将不胜感激。

你将指针映射为第二个参数,虽然在技术上是正确的,但需要你在编写它时做更多的开销。最好将结构类型简单地作为参数。当用作 function/method 参数时,结构被视为 .ByReference,并为您处理所有自动读写。所以如果你这样做,你可以省略你的 write() 调用:

boolean Module32First(Pointer hSnapshot, WinAPI.MODULEENTRY32 lpme);
boolean Module32Next(Pointer hSnapshot, WinAPI.MODULEENTRY32 lpme);

就是说,对您的 write 调用的评论指出了这里的根本原因:您需要将 dwSize 值设置为结构的大小。但是,由于 Java 对象的初始化顺序,结构在初始化 dwSize 变量时没有确定大小所需的信息,因此这部分没有为您提供正确的大小:

public int dwSize = this.size();

要解决这个问题,只需声明该变量并稍后在构造函数中设置大小,例如,

public static class MODULEENTRY32 extends Structure {
    public int dwSize;
    // other declarations
    
    public MODULEENTRY32() {
        super();
        dwSize = size();
    }   
}

此外,您对 szModuleszExePath 的映射不正确。您只在结构的内存分配中提供了指针(总共 16 个字节),但这些是固定长度的字符数组。它们应该被定义:

public char[] szModule = new char[MAX_MODULE_NAME32 + 1];
public char[] szExePath = new char[MAX_PATH];

请注意 char[] 映射是针对映射的 Unicode (_W) 版本,这是一个相当安全的假设。

但是与其自己编写,不如使用 jna-platform 中 JNA 用户提供的映射中已经存在的映射。 The MODULEENTRY32W type is already there (yes it uses DWORD so your version is simpler; but you can look at the code to see how they handled the size.) And the Module32FirstW and Module32NextW functions are also mapped。 (W 后缀用于所有现代 Windows 系统现在使用的宽字符 (Unicode)。)