java-native-access/jna · error · Win32Exception

Win32Exception from native GetLastError after…

Error message

Win32Exception from native GetLastError after QueryDosDevice returned 0

What it means

queryDosDevice resolves a DOS device name (e.g. "C:") to its native target via the Win32 QueryDosDevice API. A return size of 0 signals failure, and the wrapper throws Win32Exception carrying GetLastError — most often ERROR_FILE_NOT_FOUND because the device name does not exist. The result is parsed into a list of null-terminated strings.

Solutions

  1. Validate the device name exists first (e.g. new File("C:\\").exists() or QueryDosDevice with null to list all names).
  2. Pass the device name WITHOUT a trailing backslash ("C:" not "C:\\").
  3. Increase maxTargetSize (e.g. 1024) if ERROR_INSUFFICIENT_BUFFER is the reported code.
  4. Catch Win32Exception and treat ERROR_FILE_NOT_FOUND as 'device absent' rather than a hard failure.

Example fix

// before
List<String> targets = Kernel32Util.queryDosDevice("Z:", 260); // may throw if Z: unmounted

// after
try {
    List<String> targets = Kernel32Util.queryDosDevice("Z:", 1024);
} catch (Win32Exception e) {
    if (e.getErrorCode() == WinError.ERROR_FILE_NOT_FOUND) {
        targets = Collections.emptyList(); // drive not mapped
    } else {
        throw e;
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (lpszDeviceName.endsWith("\\")) {
    throw new IllegalArgumentException("Device name must not end with backslash: " + lpszDeviceName);
}

Try / catch

try {
    return Kernel32Util.queryDosDevice(name, 1024);
} catch (Win32Exception e) {
    if (e.getErrorCode() == WinError.ERROR_FILE_NOT_FOUND) {
        return Collections.emptyList();
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling Kernel32Util.queryDosDevice(lpszDeviceName, maxTargetSize) when QueryDosDevice returns 0 — the DOS device name does not exist (typo or drive letter not mounted), the buffer (maxTargetSize) is too small for the target path, or the name contains invalid characters.

Common situations: Checking a mapped drive that was disconnected/unmounted; querying subst or symbolically linked device names that were removed; passing a name with a trailing backslash when the API expects none; using too small maxTargetSize for very long UNC targets.

Related errors


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

Appendix: source

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

            buffer.append(string).append('\0');
        buffer.append('\0');
        if (! Kernel32.INSTANCE.WritePrivateProfileSection(appName, buffer.toString(), fileName)) {
            throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
        }
    }

    /**
     * Invokes the {@link Kernel32#QueryDosDevice(String, char[], int)} method
     * and parses the result
     * @param lpszDeviceName The device name
     * @param maxTargetSize The work buffer size to use for the query
     * @return The parsed result
     */
    public static final List<String> queryDosDevice(String lpszDeviceName, int maxTargetSize) {
        char[] lpTargetPath = new char[maxTargetSize];
        int dwSize = Kernel32.INSTANCE.QueryDosDevice(lpszDeviceName, lpTargetPath, lpTargetPath.length);
        if (dwSize == 0) {
            throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
        }

        return Native.toStringList(lpTargetPath, 0, dwSize);
    }

    /**
     * Invokes and parses the result of {@link Kernel32#GetVolumePathNamesForVolumeName(String, char[], int, IntByReference)}
     * @param lpszVolumeName The volume name
     * @return The parsed result
     * @throws Win32Exception If failed to retrieve the required information
     */
    public static final List<String> getVolumePathNamesForVolumeName(String lpszVolumeName) {
        char[] lpszVolumePathNames = new char[WinDef.MAX_PATH + 1];
        IntByReference lpcchReturnLength = new IntByReference();

        if (!Kernel32.INSTANCE.GetVolumePathNamesForVolumeName(lpszVolumeName, lpszVolumePathNames, lpszVolumePathNames.length, lpcchReturnLength)) {
            int hr = Kernel32.INSTANCE.GetLastError();
            if (hr != WinError.ERROR_MORE_DATA) {

View on GitHub (pinned to d036ad9781)