调用 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();
}
}
此外,您对 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 (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)。)
我正在使用 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();
}
}
此外,您对 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 (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)。)