java-native-access/jna · error · Win32Exception
Win32Exception from native GetLastError after…
Error message
Win32Exception from native GetLastError after CreateToolhelp32Snapshot returned null
What it means
Kernel32Util.getModules throws this Win32Exception when Kernel32.INSTANCE.CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processID) returns null. GetLastError carries the native reason — commonly ERROR_ACCESS_DENIED (5) for protected processes, or ERROR_INVALID_PARAMETER (87) for a nonexistent process ID.
Solutions
- Validate the PID is alive before snapshotting (e.g. via Kernel32 OpenProcess or tasklist)
- Run the caller elevated (Administrator) when targeting processes from other sessions/users
- Catch Win32Exception and check errorCode: 5 = need elevation, 87 = invalid PID
- Retry once on transient snapshot failures if the PID is expected to exist
Example fix
// before
List<Tlhelp32.MODULEENTRY32W> mods = Kernel32Util.getModules(pid);
// after
try {
List<Tlhelp32.MODULEENTRY32W> mods = Kernel32Util.getModules(pid);
} catch (Win32Exception e) {
if (e.getErrorCode().intValue() == WinError.ERROR_ACCESS_DENIED) {
throw new IllegalStateException("Run elevated to inspect PID " + pid, e);
}
throw e;
} Defensive patterns
Strategy: try-catch
Validate before calling
// Java: check PID liveness before snapshotting HANDLE proc = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_LIMITED_INFORMATION, false, new DWORD(pid)); if (proc == null) throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); // PID dead or access denied Kernel32.INSTANCE.CloseHandle(proc);
Type guard
boolean pidAlive(int pid) {
HANDLE p = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_LIMITED_INFORMATION, false, new DWORD(pid));
if (p == null) return false;
Kernel32.INSTANCE.CloseHandle(p);
return true;
} Try / catch
try {
List<Tlhelp32.MODULEENTRY32W> mods = Kernel32Util.getModules(pid);
} catch (Win32Exception e) {
if (e.getErrorCode().intValue() == WinError.ERROR_ACCESS_DENIED) {
throw new IllegalStateException("Elevation required for PID " + pid, e);
}
if (e.getErrorCode().intValue() == WinError.ERROR_INVALID_PARAMETER) {
return Collections.emptyList(); // process gone
}
throw e;
} Prevention
- Verify the PID exists immediately before snapshotting
- Run elevated when inspecting processes from other users/sessions
- Expect access denial for PPL/protected processes and handle it explicitly
- Treat missing-PID (87) as an expected outcome in process-watch code
When it happens
Trigger: Calling Kernel32Util.getModules(processID) with a PID that does not exist, a system/protected process (e.g. services running as SYSTEM, PPL processes), or insufficient privileges of the calling process for TH32CS_SNAPMODULE.
Common situations: Enumerating modules of another user's process without elevation, snapshotting a PID that already exited (race between lookup and snapshot), listing modules of antivirus/PPL-protected processes.
Understand the failure class
Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.
Related errors
- Win32Exception from GetLastError after Module32NextW failed…
- Win32Exception from native GetLastError after…
- LockResource returned null.
- Win32Exception from native GetLastError after…
- Win32Exception from native GetLastError after…
AI-assisted analysis of java-native-access/jna@d036ad9781 (2026-09-12).
Data as JSON: /api/errors/d4fb1ef4d80c2625.
Report an issue: GitHub.
Appendix: source
Thrown at contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java:1172
}
if (err != null) {
throw err;
}
return result;
}
/**
* Returns all the executable modules for a given process ID.<br>
*
* @param processID
* The process ID to get executable modules for
* @return All the modules in the process.
*/
public static List<Tlhelp32.MODULEENTRY32W> getModules(int processID) {
HANDLE snapshot = Kernel32.INSTANCE.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPMODULE, new DWORD(processID));
if (snapshot == null) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
Win32Exception we = null;
try {
Tlhelp32.MODULEENTRY32W first = new Tlhelp32.MODULEENTRY32W();
if (!Kernel32.INSTANCE.Module32FirstW(snapshot, first)) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
List<Tlhelp32.MODULEENTRY32W> modules = new ArrayList<>();
modules.add(first);
Tlhelp32.MODULEENTRY32W next = new Tlhelp32.MODULEENTRY32W();
while (Kernel32.INSTANCE.Module32NextW(snapshot, next)) {
modules.add(next);
next = new Tlhelp32.MODULEENTRY32W();
}View on GitHub (pinned to d036ad9781)