java-native-access/jna · error · Win32Exception

Win32Exception(Kernel32.INSTANCE.GetLastError())

Error message

Win32Exception(Kernel32.INSTANCE.GetLastError())

What it means

After calling NetGetDCName, getDCName frees the native buffer with NetApiBufferFree in a finally block. If the free call itself fails, the resulting GetLastError() code is wrapped in a Win32Exception and thrown. Note this can mask the original return value or any in-flight exception from the try block.

Source

Thrown at contrib/platform/src/com/sun/jna/platform/win32/Netapi32Util.java:128

     * @param serverName
     *     Specifies the DNS or NetBIOS name of the remote server on which the function is
     *     to execute.
     * @param domainName
     *     Specifies the name of the domain.
     * @return
     *  Name of the primary domain controller.
     */
    public static String getDCName(String serverName, String domainName) {
        PointerByReference bufptr = new PointerByReference();
        try {
            int rc = Netapi32.INSTANCE.NetGetDCName(serverName, domainName, bufptr);
            if (LMErr.NERR_Success != rc) {
                throw new Win32Exception(rc);
            }
            return bufptr.getValue().getWideString(0);
        } finally {
            if (W32Errors.ERROR_SUCCESS != Netapi32.INSTANCE.NetApiBufferFree(bufptr.getValue())) {
                throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
            }
        }
    }

    /**
     * Return the domain/workgroup join status for a computer.
     * @return Join status.
     */
    public static int getJoinStatus() {
        return getJoinStatus(null);
    }

    /**
     * Return the domain/workgroup join status for a computer.
     * @param computerName Computer name.
     * @return Join status.
     */
    public static int getJoinStatus(String computerName) {

View on GitHub (pinned to d036ad9781)

Solutions

  1. Treat this exception as secondary: the real failure is usually in NetGetDCName; capture and log both errors.
  2. Guard the finally block: only free when bufptr.getValue() != Pointer.NULL.
  3. Upgrade JNA — newer versions harden these cleanup paths against NULL buffers.
  4. If it persists, check for heap/native memory corruption elsewhere in the process.

Example fix

// before
} finally {
    if (W32Errors.ERROR_SUCCESS != Netapi32.INSTANCE.NetApiBufferFree(bufptr.getValue())) {
        throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
    }
}
// after
} finally {
    if (bufptr.getValue() != Pointer.NULL
            && W32Errors.ERROR_SUCCESS != Netapi32.INSTANCE.NetApiBufferFree(bufptr.getValue())) {
        throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Only call getDCName when it can succeed; a failed call is what corrupts cleanup
PointerByReference bufptr = new PointerByReference();
int rc = Netapi32.INSTANCE.NetGetDCName(null, domain, bufptr);
boolean ok = (rc == LMErr.NERR_Success) && bufptr.getValue() != Pointer.NULL;

Try / catch

try {
    return Netapi32Util.getDCName(server, domain);
} catch (Win32Exception e) {
    LOG.warn("getDCName/cleanup failed, rc=" + e.getErrorCode());
    return null; // treat as 'no DC available'
}

Prevention

When it happens

Trigger: NetApiBufferFree returns non-ERROR_SUCCESS while cleaning up the DC-name buffer — e.g. bufptr.getValue() is NULL because NetGetDCName failed and never allocated a buffer, or the pointer is already invalid.

Common situations: getDCName failing (e.g. no DC found) so the finally block frees a NULL/invalid pointer and throws a second, confusing exception that hides the real cause.

Related errors


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