java-native-access/jna · error · Win32Exception

Win32Exception from native GetLastError after…

Error message

Win32Exception from native GetLastError after Module32FirstW failed

What it means

Kernel32Util.getModules throws this Win32Exception when Kernel32.INSTANCE.Module32FirstW returns false after a valid snapshot was created. The native GetLastError code explains why the first module entry could not be retrieved (e.g. ERROR_ACCESS_DENIED or a snapshot invalidated because the target process exited).

Solutions

  1. Treat this as a race: catch Win32Exception and skip the process if it likely exited
  2. Check errorCode and map ERROR_NO_MORE_FILES/ERROR_INVALID_PARAMETER to 'empty module list' rather than a hard failure
  3. Retry getModules once after a short delay for volatile PIDs
  4. Re-check the PID's existence (OpenProcess) before retrying

Example fix

// before
List<Tlhelp32.MODULEENTRY32W> mods = Kernel32Util.getModules(pid);
// after
List<Tlhelp32.MODULEENTRY32W> mods;
try {
    mods = Kernel32Util.getModules(pid);
} catch (Win32Exception e) {
    mods = Collections.emptyList(); // process exited before first module read
}
Defensive patterns

Strategy: retry

Validate before calling

HANDLE proc = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_LIMITED_INFORMATION, false, new DWORD(pid));
if (proc == null) return Collections.emptyList(); // target exited before snapshot
Kernel32.INSTANCE.CloseHandle(proc);

Type guard

null

Try / catch

try {
    mods = Kernel32Util.getModules(pid);
} catch (Win32Exception e) {
    // process likely exited between snapshot and Module32FirstW
    mods = Collections.emptyList();
}

Prevention

When it happens

Trigger: Calling Kernel32Util.getModules(processID) where CreateToolhelp32Snapshot succeeded but Module32FirstW fails — typically because the target process terminated between snapshot creation and the first read, or the module list became inaccessible.

Common situations: Short-lived processes exiting between snapshot and Module32FirstW; processes still initializing with no modules listed yet; permission changes on the target process.

Related errors


AI-assisted analysis of java-native-access/jna@d036ad9781 (2026-09-12). Data as JSON: /api/errors/35f189258b173229. Report an issue: GitHub.

Appendix: source

Thrown at contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java:1180

    /**
     * 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();
            }

            int lastError = Kernel32.INSTANCE.GetLastError();
            // if we got a false from Module32Next,
            // check to see if it returned false because we're genuinely done
            // or if something went wrong.
            if (lastError != W32Errors.ERROR_SUCCESS && lastError != W32Errors.ERROR_NO_MORE_FILES) {
                throw new Win32Exception(lastError);
            }

View on GitHub (pinned to d036ad9781)