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
- Validate the device name exists first (e.g. new File("C:\\").exists() or QueryDosDevice with null to list all names).
- Pass the device name WITHOUT a trailing backslash ("C:" not "C:\\").
- Increase maxTargetSize (e.g. 1024) if ERROR_INSUFFICIENT_BUFFER is the reported code.
- 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
- Strip trailing backslashes from device names.
- Use a generous buffer size (>=1024).
- Treat ERROR_FILE_NOT_FOUND as 'device not mounted', not a crash.
- Validate mapped drives still exist before use.
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
- Bad volume GUID path format:
- Expected GetTokenInformation to fail with…
- Failed to find privilege
- LookupAccountNameW was expected to fail with…
- Win32Exception from GetLastError after…
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)