【问题标题】:Calling Module32First Gives Invalid Parameter (Error Code 87)调用 Module32First 给出无效参数(错误代码 87)
【发布时间】:2021-06-09 16:43:35
【问题描述】:

我正在使用 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 映射器类:

    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也是有效的,也是正确获取的。

任何帮助将不胜感激。

【问题讨论】:

    标签: java jna


    【解决方案1】:

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

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

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

    public int dwSize = this.size();
    

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

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

    此外,您对szModule 和szExePath 的映射不正确。您只在结构的内存分配中提供了指针(总共 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(是的,它使用DWORD,因此您的版本更简单;但您可以查看代码以了解它们如何处理大小。)还有Module32FirstW 和Module32NextW 函数are also mapped。 (W 后缀是所有现代 Windows 系统现在都使用的宽字符 (Unicode)。)

    【讨论】:

    • 是的,我只是混淆了单词。我的意思是说 Module32First。我将编辑我的帖子以更正。
    • 嘿,更新了。我完全按照你的建议做了 - dwSize 在构造函数中初始化,并通过引用 MODULEENTRY32 结构传入。但是,运行程序后,我仍然返回“false”,并且 GetLastError 的错误代码为 87。
    • 检查您的映射。 szModule 和szExePath 这两个字符串是固定长度的字符数组。那是 Java 中用于 unicode 的 char[]。为什么不使用我链接的已经写好的?
    • 是的,但我认为它会映射到字符串,但我想我错了。 JNA 在他们的 GitHub 页面上有一个“映射”自述文件,但他们只提供了常见的映射类型,所以我不得不猜测。我会尝试修复这些映射并让您知道。我决定做自己的结构只是为了学习目的,因为一些映射的函数和结构不可用。
    • 优秀。不要忘记 szModule 上的空终止符 +1。
    猜你喜欢
    • 1970-01-01
    • 2011-11-05
    • 1970-01-01
    • 2020-04-20
    • 2013-02-09
    • 2021-10-09
    • 1970-01-01
    • 2012-05-31
    • 1970-01-01
    相关资源
    最近更新 更多