diff --git a/CHANGES.md b/CHANGES.md
index a0d516b6b1..2ea2bbc6ab 100755
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -48,6 +48,7 @@ Features
* [#434](https://github.com/twall/jna/pull/434): Added GetEnvironmentStrings to 'com.sun.jna.platform.win32.Kernel32' - [@lgoldstein](https://github.com/lgoldstein).
* Loosen OSGI OS name matching to accommodate Windows 8 family - Niels Bertram.
* [#436] (https://github.com/twall/jna/pull/469): Added basic Pdh API implementation to 'com.sun.jna.platform.win32' - [@lgoldstein](https://github.com/lgoldstein).
+* [#481] (https://github.com/twall/jna/pull/481): Added volume management functions to 'com.sun.jna.platform.win32' - [@lgoldstein](https://github.com/lgoldstein).
Bug Fixes
---------
diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java
index 3b72d6fbe3..1ffb32eb8b 100644
--- a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java
+++ b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java
@@ -324,8 +324,8 @@ boolean ReadFile(HANDLE hFile, Buffer lpBuffer, int nNumberOfBytesToRead,
void SetLastError(int dwErrCode);
/**
- * The GetDriveType function determines whether a disk drive is a removable,
- * fixed, CD-ROM, RAM disk, or network drive.
+ * Determines whether a disk drive is a removable, fixed, CD-ROM, RAM
+ * disk, or network drive.
*
* @param lpRootPathName
* Pointer to a null-terminated string that specifies the root
@@ -333,6 +333,7 @@ boolean ReadFile(HANDLE hFile, Buffer lpBuffer, int nNumberOfBytesToRead,
* backslash is required. If this parameter is NULL, the function
* uses the root of the current directory.
* @return The return value specifies the type of drive.
+ * @see GetDriveType
*/
int GetDriveType(String lpRootPathName);
@@ -2601,4 +2602,268 @@ boolean SystemTimeToTzSpecificLocalTime(TIME_ZONE_INFORMATION lpTimeZone,
* documentation
*/
SIZE_T VirtualQueryEx(HANDLE hProcess, Pointer lpAddress, MEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength);
+
+ /**
+ * Defines, redefines, or deletes MS-DOS device names.
+ * @param dwFlags The controllable aspects of the function - see the
+ * various {@code DDD_XXX} constants
+ * @param lpDeviceName The MS-DOS device name string specifying the device
+ * the function is defining, redefining, or deleting. The device name string
+ * must not have a colon as the last character, unless a drive letter is
+ * being defined, redefined, or deleted. For example, drive {@code C} would
+ * be the string "C:". In no case is a trailing backslash
+ * ("\") allowed.
+ * @param lpTargetPath The path string that will implement this device.
+ * The string is an MS-DOS path string unless the {@code DDD_RAW_TARGET_PATH}
+ * flag is specified, in which case this string is a path string.
+ * @return {@code true} if succeeds. If fails then call {@link #GetLastError()}
+ * to get extended error information
+ * @see DefineDosDevice
+ */
+ boolean DefineDosDevice(int dwFlags, String lpDeviceName, String lpTargetPath);
+
+ /**
+ * Retrieves information about MS-DOS device names
+ * @param lpDeviceName An MS-DOS device name string specifying the target
+ * of the query. The device name cannot have a trailing backslash; for
+ * example, use "C:", not "C:\". This parameter can be
+ * NULL. In that case, the function will store a list of all existing MS-DOS
+ * device names into the buffer.
+ * @param lpTargetPath A buffer that will receive the result of the query.
+ * The function fills this buffer with one or more null-terminated strings.
+ * The final null-terminated string is followed by an additional NULL. If
+ * device name is non-NULL, the function retrieves information about the
+ * particular MS-DOS device. The first null-terminated string stored into
+ * the buffer is the current mapping for the device. The other null-terminated
+ * strings represent undeleted prior mappings for the device. Each
+ * null-terminated string stored into the buffer is the name of an existing
+ * MS-DOS device, for example, {@code \Device\HarddiskVolume1} or {@code \Device\Floppy0}.
+ * @param ucchMax The maximum number of characters that can be stored into the buffer
+ * @return If the function succeeds, the return value is the number of characters stored
+ * into the buffer, otherwise zero. Use {@link #GetLastError()} to get extended
+ * error information. If the buffer is too small, the function fails and the last error
+ * code is {@code ERROR_INSUFFICIENT_BUFFER}.
+ * @see QueryDosDevice
+ */
+ int QueryDosDevice(String lpDeviceName, char[] lpTargetPath, int ucchMax);
+
+ /**
+ * Retrieves the name of a mounted folder on the specified volume - used
+ * to begin scanning the mounted folders on a volume
+ * @param lpszRootPathName A volume GUID path for the volume to scan for
+ * mounted folders. A trailing backslash is required.
+ * @param lpszVolumeMountPoint A buffer that receives the name of the first
+ * mounted folder that is found.
+ * @param cchBufferLength The length of the buffer that receives the path
+ * to the mounted folder
+ * @return If succeeds, a search handle used in a subsequent call to the
+ * FindNextVolumeMountPoint and FindVolumeMountPointClose
+ * functions. Otherwise, the return value is the {@link #INVALID_HANDLE_VALUE}.
+ * To get extended error information, call {@link #GetLastError()}.
+ * @see FindFirstVolumeMountPoint
+ */
+ HANDLE FindFirstVolumeMountPoint(String lpszRootPathName, char[] lpszVolumeMountPoint, int cchBufferLength);
+
+ /**
+ * Continues a mounted folder search started by a call to the
+ * {@link #FindFirstVolumeMountPoint(String, char[], int)} function - finds one
+ * (next) mounted folder per call.
+ * @param hFindVolumeMountPoint A mounted folder search handle returned by
+ * a previous call to the {@link #FindFirstVolumeMountPoint(String, char[], int)}
+ * function.
+ * @param lpszVolumeMountPoint A buffer that receives the name of the (next)
+ * mounted folder that is found.
+ * @param cchBufferLength The length of the buffer that receives the path
+ * to the mounted folder
+ * @return {@code true} if succeeds. If fails then call {@link #GetLastError()}
+ * to get extended error information. If no more mount points found then the reported
+ * error is {@code ERROR_NO_MORE_FILES}. In this case, simply call
+ * {@link #FindVolumeMountPointClose(com.sun.jna.platform.win32.WinNT.HANDLE)}
+ * @see FindNextVolumeMountPoint
+ */
+ boolean FindNextVolumeMountPoint(HANDLE hFindVolumeMountPoint, char[] lpszVolumeMountPoint, int cchBufferLength);
+
+ /**
+ * Closes the specified mounted folder search handle.
+ * @param hFindVolumeMountPoint A mounted folder search handle returned by
+ * a previous call to the {@link #FindFirstVolumeMountPoint(String, char[], int)}
+ * function.
+ * @return {@code true} if succeeds. If fails then call {@link #GetLastError()}
+ * to get extended error information
+ * @see FindVolumeMountPointClose
+ */
+ boolean FindVolumeMountPointClose(HANDLE hFindVolumeMountPoint);
+
+ /**
+ * Retrieves a volume GUID path for the volume that is associated with the
+ * specified volume mount point (drive letter, volume GUID path, or mounted
+ * folder).
+ * @param lpszVolumeMountPoint A string that contains the path of a mounted
+ * folder (e.g., "Y:\MountX\") or a drive letter (for example,
+ * "X:\"). The string must end with a trailing backslash.
+ * @param lpszVolumeName A buffer that receives the volume GUID path - if
+ * there is more than one volume GUID path for the volume, only the first
+ * one in the mount manager's cache is returned.
+ * @param cchBufferLength The length of the output buffer - a reasonable size
+ * for the buffer to accommodate the largest possible volume GUID path is
+ * at 50 characters
+ * @return {@code true} if succeeds. If fails then call {@link #GetLastError()}
+ * to get extended error information
+ * @see GetVolumeNameForVolumeMountPoint
+ */
+ boolean GetVolumeNameForVolumeMountPoint(String lpszVolumeMountPoint, char[] lpszVolumeName, int cchBufferLength);
+
+ /**
+ * Sets the label of a file system volume.
+ * @param lpRootPathName The volume's drive letter (for example, {@code X:\})
+ * or the path of a mounted folder that is associated with the volume (for
+ * example, {@code Y:\MountX\}). The string must end with a trailing backslash.
+ * If this parameter is NULL, the root of the current directory is used.
+ * @param lpVolumeName The new label for the volume. If this parameter is NULL,
+ * the function deletes any existing label from the specified volume and does
+ * not assign a new label.
+ * @return {@code true} if succeeds. If fails then call {@link #GetLastError()}
+ * to get extended error information
+ * @see SetVolumeLabel
+ */
+ boolean SetVolumeLabel(String lpRootPathName, String lpVolumeName);
+
+ /**
+ * Associates a volume with a drive letter or a directory on another volume.
+ * @param lpszVolumeMountPoint The user-mode path to be associated with the
+ * volume. This may be a drive letter (for example, "X:\") or a
+ * directory on another volume (for example, "Y:\MountX\"). The
+ * string must end with a trailing backslash.
+ * @param lpszVolumeName A volume GUID path for the volume.
+ * @return {@code true} if succeeds. If fails then call {@link #GetLastError()}
+ * to get extended error information
+ * @see SetVolumeMountPoint
+ */
+ boolean SetVolumeMountPoint(String lpszVolumeMountPoint, String lpszVolumeName);
+
+ /**
+ * Deletes a drive letter or mounted folder
+ * @param lpszVolumeMountPoint The drive letter or mounted folder to be deleted.
+ * A trailing backslash is required, for example, "X:\" or "Y:\MountX\".
+ * @return {@code true} if succeeds. If fails then call {@link #GetLastError()}
+ * to get extended error information
+ * @see DeleteVolumeMountPoint
+ */
+ boolean DeleteVolumeMountPoint(String lpszVolumeMountPoint);
+
+ /**
+ * @param lpRootPathName A string that contains the root directory of the
+ * volume to be described. If this parameter is {@code null}, the root of
+ * the current directory is used. A trailing backslash is required. For example,
+ * you specify "\\MyServer\MyShare\", or "C:\".
+ * @param lpVolumeNameBuffer If not {@code null} then receives the name of
+ * the specified volume. The buffer size is specified by the nVolumeNameSize
+ * parameter.
+ * @param nVolumeNameSize The length of the volume name buffer - max. size is
+ * {@link WinDef#MAX_PATH} + 1 - ignored if no volume name buffer provided
+ * @param lpVolumeSerialNumber Receives the volume serial number - can be
+ * {@code null} if the serial number is not required
+ * @param lpMaximumComponentLength Receives the maximum length of a file name
+ * component that the underlying file system supports - can be {@code null}
+ * if this data is not required
+ * @param lpFileSystemFlags Receives flags associated with the file system
+ * - can be {@code null} if this data is not required
+ * @param lpFileSystemNameBuffer If not {@code null} then receives the name
+ * of the file system. The buffer size is specified by the nFileSystemNameSize
+ * parameter.
+ * @param nFileSystemNameSize The length of the file system name buffer -
+ * max. size is {@link WinDef#MAX_PATH} + 1 - ignored if no file system name
+ * buffer provided
+ * @return {@code true} if succeeds. If fails then call {@link #GetLastError()}
+ * to get extended error information
+ * @see GetVolumeInformation
+ */
+ boolean GetVolumeInformation(String lpRootPathName,
+ char[] lpVolumeNameBuffer, int nVolumeNameSize,
+ IntByReference lpVolumeSerialNumber,
+ IntByReference lpMaximumComponentLength,
+ IntByReference lpFileSystemFlags,
+ char[] lpFileSystemNameBuffer, int nFileSystemNameSize);
+
+ /**
+ * Retrieves the volume mount point where the specified path is mounted.
+ * @param lpszFileName The input path string. Both absolute and relative
+ * file and directory names, for example "..", are acceptable in
+ * this path. If you specify a relative directory or file name without a
+ * volume qualifier, returns the drive letter of the boot volume. If this
+ * parameter is an empty string, the function fails but the last error is
+ * set to {@code ERROR_SUCCESS}.
+ * @param lpszVolumePathName Buffer receives the volume mount point for the
+ * input path.
+ * @param cchBufferLength The length of the output buffer
+ * @return {@code true} if succeeds. If fails then call {@link #GetLastError()}
+ * to get extended error information
+ * @see GetVolumePathName
+ */
+ boolean GetVolumePathName(String lpszFileName, char[] lpszVolumePathName, int cchBufferLength);
+
+ /**
+ * Retrieves a list of drive letters and mounted folder paths for the specified volume
+ * @param lpszVolumeName A volume GUID path for the volume
+ * @param lpszVolumePathNames A buffer that receives the list of drive
+ * letters and mounted folder paths. The list is an array of null-terminated
+ * strings terminated by an additional NULL character. If the buffer is
+ * not large enough to hold the complete list, the buffer holds as much of
+ * the list as possible.
+ * @param cchBufferLength The available length of the buffer - including all
+ * NULL characters.
+ * @param lpcchReturnLength If the call is successful, this parameter is the
+ * number of character copied to the buffer. Otherwise, this parameter is the
+ * size of the buffer required to hold the complete list
+ * @return {@code true} if succeeds. If fails then call {@link #GetLastError()}
+ * to get extended error information. If the buffer is not large enough to hold
+ * the complete list, the error code is {@code ERROR_MORE_DATA} and the
+ * lpcchReturnLength parameter receives the required buffer size.
+ * @see GetVolumePathNamesForVolumeName
+ */
+ boolean GetVolumePathNamesForVolumeName(String lpszVolumeName,
+ char[] lpszVolumePathNames, int cchBufferLength,
+ IntByReference lpcchReturnLength);
+
+ /**
+ * Retrieves the name of a volume on a computer - used to begin scanning the
+ * volumes of a computer
+ * @param lpszVolumeName A buffer that receives a null-terminated string that
+ * specifies a volume GUID path for the first volume that is found
+ * @param cchBufferLength The length of the buffer to receive the volume GUID path
+ * @return If the function succeeds, the return value is a search handle
+ * used in a subsequent call to the {@link #FindNextVolume(com.sun.jna.platform.win32.WinNT.HANDLE, char[], int)
+ * and {@link #FindVolumeClose(com.sun.jna.platform.win32.WinNT.HANDLE)} functions.
+ * Otherwise, the return value is the {@link #INVALID_HANDLE_VALUE}. To get
+ * extended error information, call {@link #GetLastError()}.
+ * @see FindFirstVolume
+ * @see Kernel32Util#extractVolumeGUID(String)
+ */
+ HANDLE FindFirstVolume(char[] lpszVolumeName, int cchBufferLength);
+
+ /**
+ * Continues a volume search started by a call to the {@link #FindFirstVolume(char[], int)}
+ * function - finds one volume per call.
+ * @param hFindVolume The volume search handle returned by a previous call to the
+ * {@link #FindFirstVolume(char[], int)}.
+ * @param lpszVolumeName A buffer that receives a null-terminated string that
+ * specifies a volume GUID path for the (next) path that is found
+ * @param cchBufferLength The length of the buffer to receive the volume GUID path
+ * @return {@code true} if succeeds. If fails then call {@link #GetLastError()}
+ * to get extended error information. If no more volumes found then the reported
+ * error is {@code ERROR_NO_MORE_FILES}. In this case, simply call {@link #FindVolumeClose(com.sun.jna.platform.win32.WinNT.HANDLE)}
+ * @see FindNextVolume
+ * @see Kernel32Util#extractVolumeGUID(String)
+ */
+ boolean FindNextVolume(HANDLE hFindVolume, char[] lpszVolumeName, int cchBufferLength);
+
+ /**
+ * Closes the specified volume search handle.
+ * @param hFindVolume The volume search handle returned by a previous call to the
+ * {@link #FindFirstVolume(char[], int)}.
+ * @return {@code true} if succeeds. If fails then call {@link #GetLastError()}
+ * to get extended error information
+ * @see FindVolumeClose
+ */
+ boolean FindVolumeClose(HANDLE hFindVolume);
}
diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java
index 576a61f309..90ab009d4d 100644
--- a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java
+++ b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32Util.java
@@ -15,7 +15,6 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.nio.ByteOrder;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
@@ -136,33 +135,22 @@ public static void deleteFile(String filename) {
/**
* Returns valid drives in the system.
*
- * @return An array of valid drives.
+ * @return A {@link List} of valid drives.
*/
- public static String[] getLogicalDriveStrings() {
- DWORD dwSize = Kernel32.INSTANCE.GetLogicalDriveStrings(new DWORD(0),
- null);
+ public static List getLogicalDriveStrings() {
+ DWORD dwSize = Kernel32.INSTANCE.GetLogicalDriveStrings(new DWORD(0), null);
if (dwSize.intValue() <= 0) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
char buf[] = new char[dwSize.intValue()];
dwSize = Kernel32.INSTANCE.GetLogicalDriveStrings(dwSize, buf);
- if (dwSize.intValue() <= 0) {
+ int bufSize = dwSize.intValue();
+ if (bufSize <= 0) {
throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
}
- List drives = new ArrayList();
- String drive = "";
- // the buffer is double-null-terminated
- for (int i = 0; i < buf.length - 1; i++) {
- if (buf[i] == 0) {
- drives.add(drive);
- drive = "";
- } else {
- drive += buf[i];
- }
- }
- return drives.toArray(new String[0]);
+ return Native.toStringList(buf, 0, bufSize);
}
/**
@@ -609,4 +597,76 @@ public static final void writePrivateProfileSection(final String appName, final
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 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 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) {
+ throw new Win32Exception(hr);
+ }
+
+ int required = lpcchReturnLength.getValue();
+ lpszVolumePathNames = new char[required];
+ // this time we MUST succeed
+ if (!Kernel32.INSTANCE.GetVolumePathNamesForVolumeName(lpszVolumeName, lpszVolumePathNames, lpszVolumePathNames.length, lpcchReturnLength)) {
+ throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
+ }
+ }
+
+ int bufSize = lpcchReturnLength.getValue();
+ return Native.toStringList(lpszVolumePathNames, 0, bufSize);
+ }
+
+ // prefix and suffix of a volume GUID path
+ public static final String VOLUME_GUID_PATH_PREFIX = "\\\\?\\Volume{";
+ public static final String VOLUME_GUID_PATH_SUFFIX = "}\\";
+
+ /**
+ * Parses and returns the pure GUID value of a volume name obtained
+ * from {@link Kernel32#FindFirstVolume(char[], int)} or
+ * {@link Kernel32#FindNextVolume(HANDLE, char[], int)} calls
+ *
+ * @param volumeName
+ * The volume name as returned by on of the above mentioned calls
+ * @return The pure GUID value after stripping the "\\?\" prefix and
+ * removing the trailing backslash.
+ * @throws IllegalArgumentException if bad format encountered
+ * @see Naming a Volume
+ */
+ public static final String extractVolumeGUID(String volumeGUIDPath) {
+ if ((volumeGUIDPath == null)
+ || (volumeGUIDPath.length() <= (VOLUME_GUID_PATH_PREFIX.length() + VOLUME_GUID_PATH_SUFFIX.length()))
+ || (!volumeGUIDPath.startsWith(VOLUME_GUID_PATH_PREFIX))
+ || (!volumeGUIDPath.endsWith(VOLUME_GUID_PATH_SUFFIX))) {
+ throw new IllegalArgumentException("Bad volume GUID path format: " + volumeGUIDPath);
+ }
+
+ return volumeGUIDPath.substring(VOLUME_GUID_PATH_PREFIX.length(), volumeGUIDPath.length() - VOLUME_GUID_PATH_SUFFIX.length());
+ }
}
diff --git a/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java b/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java
index e383d3d6c4..382750af2f 100644
--- a/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java
+++ b/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java
@@ -722,6 +722,19 @@ public abstract class SID_NAME_USE {
int FILE_READ_ONLY_VOLUME = 0x00080000;
int FILE_SEQUENTIAL_WRITE_ONCE = 0x00100000;
int FILE_SUPPORTS_TRANSACTIONS = 0x00200000;
+ // NOTE: These values are not supported until Windows Server 2008 R2 and Windows 7
+ int FILE_SUPPORTS_HARD_LINKS = 0x00400000;
+ int FILE_SUPPORTS_EXTENDED_ATTRIBUTES = 0x00800000;
+ int FILE_SUPPORTS_OPEN_BY_FILE_ID = 0x01000000;
+ int FILE_SUPPORTS_USN_JOURNAL = 0x02000000;
+
+
+ // The controllable aspects of the DefineDosDevice function.
+ // see https://msdn.microsoft.com/en-us/library/windows/desktop/aa363904(v=vs.85).aspx
+ int DDD_RAW_TARGET_PATH = 0x00000001;
+ int DDD_REMOVE_DEFINITION = 0x00000002;
+ int DDD_EXACT_MATCH_ON_REMOVE = 0x00000004;
+ int DDD_NO_BROADCAST_SYSTEM = 0x00000008;
/**
* The FILE_NOTIFY_INFORMATION structure describes the changes found by the
diff --git a/contrib/platform/test/com/sun/jna/platform/win32/AbstractWin32TestSupport.java b/contrib/platform/test/com/sun/jna/platform/win32/AbstractWin32TestSupport.java
index 9f7143c23d..553f2a3c00 100644
--- a/contrib/platform/test/com/sun/jna/platform/win32/AbstractWin32TestSupport.java
+++ b/contrib/platform/test/com/sun/jna/platform/win32/AbstractWin32TestSupport.java
@@ -13,6 +13,7 @@
package com.sun.jna.platform.win32;
import com.sun.jna.platform.AbstractPlatformTestSupport;
+import com.sun.jna.platform.win32.WinNT.HANDLE;
/**
* @author lgoldstein
@@ -35,11 +36,11 @@ public static final void assertCallSucceeded(String message, boolean result) {
return;
}
- int hr=Kernel32.INSTANCE.GetLastError();
+ int hr = Kernel32.INSTANCE.GetLastError();
if (hr == WinError.ERROR_SUCCESS) {
fail(message + " failed with unknown reason code");
} else {
- fail(message + " failed: hr=0x" + Integer.toHexString(hr));
+ fail(message + " failed: hr=" + hr + " - 0x" + Integer.toHexString(hr));
}
}
@@ -58,4 +59,26 @@ public static final void assertErrorSuccess(String message, int statusCode, bool
assertEquals(message, WinError.ERROR_SUCCESS, statusCode);
}
}
+
+ /**
+ * Makes sure that the handle argument is not {@code null} or {@link WinBase#INVALID_HANDLE_VALUE}.
+ * If invalid handle detected, then it invokes {@link Kernel32#GetLastError()}
+ * in order to display the error code
+ * @param message Message to display if bad handle
+ * @param handle The {@link HANDLE} to test
+ * @return The same as the input handle if good handle - otherwise does
+ * not return and throws an assertion error
+ */
+ public static final HANDLE assertValidHandle(String message, HANDLE handle) {
+ if ((handle == null) || WinBase.INVALID_HANDLE_VALUE.equals(handle)) {
+ int hr = Kernel32.INSTANCE.GetLastError();
+ if (hr == WinError.ERROR_SUCCESS) {
+ fail(message + " failed with unknown reason code");
+ } else {
+ fail(message + " failed: hr=" + hr + " - 0x" + Integer.toHexString(hr));
+ }
+ }
+
+ return handle;
+ }
}
diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java
index 61562de9cf..ed2173d650 100644
--- a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java
+++ b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java
@@ -399,13 +399,6 @@ public void testGlobalMemoryStatusEx() {
assertEquals(0, lpBuffer.ullAvailExtendedVirtual.intValue());
}
- public void testGetLogicalDriveStrings() {
- DWORD dwSize = Kernel32.INSTANCE.GetLogicalDriveStrings(new DWORD(0), null);
- assertTrue(dwSize.intValue() > 0);
- char buf[] = new char[dwSize.intValue()];
- assertTrue(Kernel32.INSTANCE.GetLogicalDriveStrings(dwSize, buf).intValue() > 0);
- }
-
public void testGetDiskFreeSpaceEx() {
LARGE_INTEGER.ByReference lpFreeBytesAvailable = new LARGE_INTEGER.ByReference();
LARGE_INTEGER.ByReference lpTotalNumberOfBytes = new LARGE_INTEGER.ByReference();
diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java
index 3bf889db81..523d183f2a 100644
--- a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java
+++ b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32UtilTest.java
@@ -19,11 +19,12 @@
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
-
-import junit.framework.TestCase;
+import java.util.Collection;
import com.sun.jna.platform.win32.WinNT.LARGE_INTEGER;
+import junit.framework.TestCase;
+
/**
* @author dblock[at]dblock[dot]org
* @author markus[at]headcrashing[dot]eu
@@ -35,7 +36,7 @@ public static void main(String[] args) throws Exception {
System.out.println("Temp path: " + Kernel32Util.getTempPath());
// logical drives
System.out.println("Logical drives: ");
- String[] logicalDrives = Kernel32Util.getLogicalDriveStrings();
+ Collection logicalDrives = Kernel32Util.getLogicalDriveStrings();
for(String logicalDrive : logicalDrives) {
// drive type
System.out.println(" " + logicalDrive + " ("
@@ -105,10 +106,10 @@ public void testGetTempPath() {
}
public void testGetLogicalDriveStrings() {
- String[] logicalDrives = Kernel32Util.getLogicalDriveStrings();
- assertTrue(logicalDrives.length > 0);
+ Collection logicalDrives = Kernel32Util.getLogicalDriveStrings();
+ assertTrue("No logical drives found", logicalDrives.size() > 0);
for(String logicalDrive : logicalDrives) {
- assertTrue(logicalDrive.length() > 0);
+ assertTrue("Empty logical drive name in list", logicalDrive.length() > 0);
}
}
diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32VolumeManagementFunctionsTest.java b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32VolumeManagementFunctionsTest.java
new file mode 100644
index 0000000000..aba091b126
--- /dev/null
+++ b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32VolumeManagementFunctionsTest.java
@@ -0,0 +1,188 @@
+/* Copyright (c) 2007 Timothy Wall, All Rights Reserved
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ */
+package com.sun.jna.platform.win32;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.sun.jna.Native;
+import com.sun.jna.platform.win32.WinNT.HANDLE;
+import com.sun.jna.ptr.IntByReference;
+
+public class Kernel32VolumeManagementFunctionsTest extends AbstractWin32TestSupport {
+ public Kernel32VolumeManagementFunctionsTest() {
+ super();
+ }
+
+ @Test
+ public void testQueryDosDevice() {
+ Collection logicalDrives = Kernel32Util.getLogicalDriveStrings();
+ for (String lpszDeviceName : logicalDrives) {
+ // the documentation states that the device name cannot have a trailing backslash
+ if (lpszDeviceName.charAt(lpszDeviceName.length() - 1) == File.separatorChar) {
+ lpszDeviceName = lpszDeviceName.substring(0, lpszDeviceName.length() - 1);
+ }
+
+ Collection devices = Kernel32Util.queryDosDevice(lpszDeviceName, WinBase.MAX_PATH);
+ assertTrue("No devices for " + lpszDeviceName, devices.size() > 0);
+ for (String name : devices) {
+ assertTrue("Empty device name for " + lpszDeviceName, name.length() > 0);
+// System.out.append(getCurrentTestName()).append('[').append(lpszDeviceName).append(']').append(" - ").println(name);
+ }
+ }
+ }
+
+ @Test
+ public void testGetVolumePathName() {
+ char[] lpszVolumePathName = new char[WinDef.MAX_PATH + 1];
+ for (String propName : new String[] { "java.home", "java.io.tmpdir", "user.dir", "user.home" }) {
+ String lpszFileName = System.getProperty(propName);
+ assertCallSucceeded("GetVolumePathName(" + lpszFileName + ")",
+ Kernel32.INSTANCE.GetVolumePathName(lpszFileName, lpszVolumePathName, lpszVolumePathName.length));
+ String path = Native.toString(lpszVolumePathName);
+// System.out.append(getCurrentTestName()).append('[').append(lpszFileName).append(']').append(" - ").println(path);
+ assertTrue("No volume path for " + lpszFileName, path.length() > 0);
+ }
+ }
+
+ @Test
+ public void testGetVolumeNameForVolumeMountPoint() {
+ Collection logicalDrives = Kernel32Util.getLogicalDriveStrings();
+ char[] lpVolumeNameBuffer = new char[WinDef.MAX_PATH + 1];
+ for (String lpszVolumeMountPoint : logicalDrives) {
+ // according to documentation path MUST end in backslash
+ if (lpszVolumeMountPoint.charAt(lpszVolumeMountPoint.length() - 1) != File.separatorChar) {
+ lpszVolumeMountPoint += File.separator;
+ }
+
+ int driveType = Kernel32.INSTANCE.GetDriveType(lpszVolumeMountPoint);
+ // network mapped drives fail GetVolumeNameForVolumeMountPoint call
+ if (driveType != WinBase.DRIVE_FIXED) {
+// System.out.append('\t').append('[').append(lpszVolumeMountPoint).append(']').println(" - skipped: non-fixed drive");
+ continue;
+ }
+
+ if (Kernel32.INSTANCE.GetVolumeNameForVolumeMountPoint(lpszVolumeMountPoint, lpVolumeNameBuffer, lpVolumeNameBuffer.length)) {
+ String volumeGUID = Native.toString(lpVolumeNameBuffer);
+// System.out.append(getCurrentTestName()).append('[').append(lpszVolumeMountPoint).append(']').append(" - ").println(volumeGUID);
+ assertTrue("Empty GUID for " + lpszVolumeMountPoint, volumeGUID.length() > 0);
+ } else {
+ int hr = Kernel32.INSTANCE.GetLastError();
+ if ((hr == WinError.ERROR_ACCESS_DENIED) // e.g., hidden volumes
+ || (hr == WinError.ERROR_NOT_READY)) { // e.g., DVD drive
+// System.out.append('\t').append('[').append(lpszVolumeMountPoint).append(']').append(" - skipped: reason=").println(hr);
+ continue;
+ }
+
+ fail("Cannot (error=" + hr + ") get volume information mount point " + lpszVolumeMountPoint);
+ }
+ }
+ }
+
+ @Test
+ public void testGetVolumeInformation() {
+ List logicalDrives = Kernel32Util.getLogicalDriveStrings();
+ char[] lpVolumeNameBuffer = new char[WinDef.MAX_PATH + 1];
+ char[] lpFileSystemNameBuffer = new char[WinDef.MAX_PATH + 1];
+ IntByReference lpVolumeSerialNumber = new IntByReference();
+ IntByReference lpMaximumComponentLength = new IntByReference();
+ IntByReference lpFileSystemFlags = new IntByReference();
+
+ for (int index=(-1); index < logicalDrives.size(); index++) {
+ String lpRootPathName = (index < 0) ? null /* curdir */ : logicalDrives.get(index);
+ // according to documentation path MUST end in backslash
+ if ((lpRootPathName != null) && (lpRootPathName.charAt(lpRootPathName.length() - 1) != File.separatorChar)) {
+ lpRootPathName += File.separator;
+ }
+
+ if (!Kernel32.INSTANCE.GetVolumeInformation(lpRootPathName,
+ lpVolumeNameBuffer, lpVolumeNameBuffer.length,
+ lpVolumeSerialNumber, lpMaximumComponentLength, lpFileSystemFlags,
+ lpFileSystemNameBuffer, lpFileSystemNameBuffer.length)) {
+ int hr = Kernel32.INSTANCE.GetLastError();
+ if ((hr == WinError.ERROR_ACCESS_DENIED) // e.g., network or hidden volumes
+ || (hr == WinError.ERROR_NOT_READY)) { // e.g., DVD drive
+// System.out.append('\t').append('[').append(lpRootPathName).append(']').append(" - skipped: reason=").println(hr);
+ continue;
+ }
+
+ fail("Cannot (error=" + hr + ") get volume information for " + lpRootPathName);
+ }
+
+// System.out.append(getCurrentTestName()).append('[').append(lpRootPathName).println(']');
+// System.out.append('\t').append("Volume name: ").println(Native.toString(lpVolumeNameBuffer));
+// System.out.append('\t').append("File system name: ").println(Native.toString(lpFileSystemNameBuffer));
+// System.out.append('\t').append("Serial number: ").println(lpVolumeSerialNumber.getValue());
+// System.out.append('\t').append("Max. component: ").println(lpMaximumComponentLength.getValue());
+// System.out.append('\t').append("File system flags: 0x").println(Integer.toHexString(lpFileSystemFlags.getValue()));
+ }
+ }
+
+ @Test
+ public void testEnumVolumes() {
+ char[] lpszVolumeName = new char[WinDef.MAX_PATH + 1];
+ HANDLE hFindVolume = assertValidHandle("FindFirstVolume", Kernel32.INSTANCE.FindFirstVolume(lpszVolumeName, lpszVolumeName.length));
+ try {
+ do {
+ String volumeGUID = Native.toString(lpszVolumeName);
+ testEnumVolumeMountMoints(volumeGUID);
+ testGetVolumePathNamesForVolumeName(volumeGUID);
+ } while(Kernel32.INSTANCE.FindNextVolume(hFindVolume, lpszVolumeName, lpszVolumeName.length));
+
+ int hr = Kernel32.INSTANCE.GetLastError();
+ assertEquals("Bad volumes enum termination reason", WinError.ERROR_NO_MORE_FILES, hr);
+ } finally {
+ assertCallSucceeded("FindVolumeClose", Kernel32.INSTANCE.FindVolumeClose(hFindVolume));
+ }
+ }
+
+ private void testGetVolumePathNamesForVolumeName(String lpszVolumeName) {
+ Collection paths = Kernel32Util.getVolumePathNamesForVolumeName(lpszVolumeName);
+ assertTrue("No paths for volume " + lpszVolumeName, paths.size() > 0);
+ for (String p : paths) {
+// System.out.append('\t').append("testGetVolumePathNamesForVolumeName").append('[').append(lpszVolumeName).append(']').append(" - ").println(p);
+ assertTrue("Empty path for volume " + lpszVolumeName, p.length() > 0);
+ }
+ }
+
+ private void testEnumVolumeMountMoints(String volumeGUID) {
+ char[] lpszVolumeMountPoint = new char[WinDef.MAX_PATH + 1];
+ HANDLE hFindVolumeMountPoint = Kernel32.INSTANCE.FindFirstVolumeMountPoint(volumeGUID, lpszVolumeMountPoint, lpszVolumeMountPoint.length);
+ if (WinNT.INVALID_HANDLE_VALUE.equals(hFindVolumeMountPoint)) {
+ int hr = Kernel32.INSTANCE.GetLastError();
+ if ((hr == WinError.ERROR_ACCESS_DENIED) // e.g., network or hidden volumes
+ || (hr == WinError.ERROR_NOT_READY)) { // e.g., DVD drive
+// System.out.append('\t').append('[').append(volumeGUID).append(']').append(" - skipped: reason=").println(hr);
+ return;
+ }
+
+ fail("Cannot (error=" + hr + ") open mount point search handle for " + volumeGUID);
+ }
+
+ try {
+ do {
+ String name = Native.toString(lpszVolumeMountPoint);
+ assertTrue("Empty mount point for " + volumeGUID, name.length() > 0);
+// System.out.append('\t').append("testEnumVolumeMountMoints").append('[').append(volumeGUID).append(']').append(" - ").println(name);
+ } while(Kernel32.INSTANCE.FindNextVolumeMountPoint(hFindVolumeMountPoint, lpszVolumeMountPoint, lpszVolumeMountPoint.length));
+
+ int hr = Kernel32.INSTANCE.GetLastError();
+ assertEquals("Mount points enum termination reason for " + volumeGUID, WinError.ERROR_NO_MORE_FILES, hr);
+ } finally {
+ assertCallSucceeded("FindVolumeMountPointClose(" + volumeGUID + ")", Kernel32.INSTANCE.FindVolumeMountPointClose(hFindVolumeMountPoint));
+ }
+ }
+}
diff --git a/src/com/sun/jna/Native.java b/src/com/sun/jna/Native.java
index 445a60f60a..53d99430ea 100644
--- a/src/com/sun/jna/Native.java
+++ b/src/com/sun/jna/Native.java
@@ -101,6 +101,7 @@ public final class Native implements Version {
private static Map libraries = new WeakHashMap();
private static final UncaughtExceptionHandler DEFAULT_HANDLER =
new UncaughtExceptionHandler() {
+ @Override
public void uncaughtException(Callback c, Throwable e) {
System.err.println("JNA: Callback " + c + " threw the following exception:");
e.printStackTrace();
@@ -168,12 +169,13 @@ public static float parseVersion(String v) {
/** Force a dispose when the Native class is GC'd. */
private static final Object finalizer = new Object() {
+ @Override
protected void finalize() {
dispose();
}
};
- /** Properly dispose of JNA functionality.
+ /** Properly dispose of JNA functionality.
Called when this class is finalized and also from JNI when
JNA's native shared library is unloaded.
*/
@@ -244,6 +246,7 @@ private Native() { }
*
* @deprecated Last error is always preserved
*/
+ @Deprecated
public static void setPreserveLastError(boolean enable) { }
/** Indicates whether the system last error result is preserved
@@ -253,6 +256,7 @@ public static void setPreserveLastError(boolean enable) { }
*
* @deprecated Last error is always preserved
*/
+ @Deprecated
public static boolean getPreserveLastError() { return true; }
/** Utility method to get the native window ID for a Java {@link Window}
@@ -307,51 +311,126 @@ public static Pointer getDirectBufferPointer(Buffer b) {
private static native long _getDirectBufferPointer(Buffer b);
- /** Obtain a Java String from the given native byte array. If there is
+ /**
+ * Obtain a Java String from the given native byte array. If there is
* no NUL terminator, the String will comprise the entire array. The
* encoding is obtained from {@link #getDefaultStringEncoding()}.
+ *
+ * @param buf The buffer containing the encoded bytes
+ * @see #toString(byte[], String)
*/
public static String toString(byte[] buf) {
return toString(buf, getDefaultStringEncoding());
}
- /** Obtain a Java String from the given native byte array, using the given
+ /**
+ * Obtain a Java String from the given native byte array, using the given
* encoding. If there is no NUL terminator, the String will comprise the
- * entire array. If the encoding
parameter is null,
- * the platform default encoding will be used.
+ * entire array.
+ *
+ * @param buf The buffer containing the encoded bytes
+ * @param encoding The encoding name - if {@code null} then the platform
+ * default encoding will be used
*/
public static String toString(byte[] buf, String encoding) {
- String s = null;
+ int len = buf.length;
+ // find out the effective length
+ for (int index = 0; index < len; index++) {
+ if (buf[index] == 0) {
+ len = index;
+ break;
+ }
+ }
+
+ if (len == 0) {
+ return "";
+ }
+
if (encoding != null) {
try {
- s = new String(buf, encoding);
+ return new String(buf, 0, len, encoding);
}
catch(UnsupportedEncodingException e) {
System.err.println("JNA Warning: Encoding '"
+ encoding + "' is unsupported");
}
}
- if (s == null) {
- System.err.println("JNA Warning: Decoding with fallback " + System.getProperty("file.encoding"));
- s = new String(buf);
+
+ System.err.println("JNA Warning: Decoding with fallback " + System.getProperty("file.encoding"));
+ return new String(buf, 0, len);
+ }
+
+ /**
+ * Obtain a Java String from the given native wchar_t array. If there is
+ * no NUL terminator, the String will comprise the entire array.
+ *
+ * @param buf The buffer containing the characters
+ */
+ public static String toString(char[] buf) {
+ int len = buf.length;
+ for (int index = 0; index < len; index++) {
+ if (buf[index] == '\0') {
+ len = index;
+ break;
+ }
}
- int term = s.indexOf(0);
- if (term != -1) {
- s = s.substring(0, term);
+
+ if (len == 0) {
+ return "";
+ } else {
+ return new String(buf, 0, len);
}
- return s;
}
- /** Obtain a Java String from the given native wchar_t array. If there is
- * no NUL terminator, the String will comprise the entire array.
+ /**
+ * Converts a "list" of strings each null terminated
+ * into a {@link List} of {@link String} values. The end of the
+ * list is signaled by an extra NULL value at the end or by the
+ * end of the buffer.
+ * @param buf The buffer containing the strings
+ * @return A {@link List} of all the strings in the buffer
+ * @see #toStringList(char[], int, int)
*/
- public static String toString(char[] buf) {
- String s = new String(buf);
- int term = s.indexOf(0);
- if (term != -1) {
- s = s.substring(0, term);
+ public static List toStringList(char[] buf) {
+ return toStringList(buf, 0, buf.length);
+ }
+
+ /**
+ * Converts a "list" of strings each null terminated
+ * into a {@link List} of {@link String} values. The end of the
+ * list is signaled by an extra NULL value at the end or by the
+ * end of the data.
+ * @param buf The buffer containing the strings
+ * @param offset Offset to start parsing
+ * @param len The total characters to parse
+ * @return A {@link List} of all the strings in the buffer
+ */
+ public static List toStringList(char[] buf, int offset, int len) {
+ List list = new ArrayList();
+ int lastPos = offset;
+ int maxPos = offset + len;
+ for (int curPos = offset; curPos < maxPos; curPos++) {
+ if (buf[curPos] != '\0') {
+ continue;
+ }
+
+ // check if found the extra null terminator
+ if (lastPos == curPos) {
+ return list;
+ }
+
+ String value = new String(buf, lastPos, curPos - lastPos);
+ list.add(value);
+ lastPos = curPos + 1; // skip the '\0'
}
- return s;
+
+ // This point is reached if there is no double null terminator
+ if (lastPos < maxPos) {
+ String value = new String(buf, lastPos, maxPos - lastPos);
+ list.add(value);
+ }
+
+ return list;
}
/** Map a library interface to the current process, providing
@@ -360,7 +439,7 @@ public static String toString(char[] buf) {
* several locations.
* @param interfaceClass
* @return an instance of the requested interface, mapped to the current
- * process.
+ * process.
* @throws UnsatisfiedLinkError if the library cannot be found or
* dependent libraries are missing.
*/
@@ -377,7 +456,7 @@ public static Object loadLibrary(Class interfaceClass) {
* @param interfaceClass
* @param options Map of library options
* @return an instance of the requested interface, mapped to the current
- * process.
+ * process.
* @throws UnsatisfiedLinkError if the library cannot be found or
* dependent libraries are missing.
*/
@@ -393,7 +472,7 @@ public static Object loadLibrary(Class interfaceClass, Map options) {
* @param name
* @param interfaceClass
* @return an instance of the requested interface, mapped to the indicated
- * native library.
+ * native library.
* @throws UnsatisfiedLinkError if the library cannot be found or
* dependent libraries are missing.
*/
@@ -412,7 +491,7 @@ public static Object loadLibrary(String name, Class interfaceClass) {
* @param interfaceClass
* @param options Map of library options
* @return an instance of the requested interface, mapped to the indicated
- * native library.
+ * native library.
* @throws UnsatisfiedLinkError if the library cannot be found or
* dependent libraries are missing.
*/
@@ -559,7 +638,7 @@ private static Object lookupField(Class mappingClass, String fieldName, Class re
return null;
}
catch (Exception e) {
- throw new IllegalArgumentException(fieldName + " must be a public field of type "
+ throw new IllegalArgumentException(fieldName + " must be a public field of type "
+ resultClass.getName() + " ("
+ e + "): " + mappingClass);
}
@@ -572,9 +651,9 @@ public static TypeMapper getTypeMapper(Class cls) {
return (TypeMapper)getLibraryOptions(cls).get(Library.OPTION_TYPE_MAPPER);
}
- /** Return the preferred Strring encoding for the given native interface.
+ /** Return the preferred Strring encoding for the given native interface.
* If there is no setting, defaults to the {@link
- * #getDefaultStringEncoding()}.
+ * #getDefaultStringEncoding()}.
* See {@link com.sun.jna.Library#OPTION_STRING_ENCODING}.
*/
public static String getStringEncoding(Class cls) {
@@ -606,7 +685,7 @@ static byte[] getBytes(String s) {
/** Return a byte array corresponding to the given String, using the given
encoding. If the encoding is not found default to the platform native
- encoding.
+ encoding.
*/
static byte[] getBytes(String s, String encoding) {
if (encoding != null) {
@@ -808,7 +887,7 @@ public static File extractFromResourcePath(String name) throws IOException {
}
/** Attempt to extract a native library from the resource path using the
- * given class loader.
+ * given class loader.
* @param name Base name of native library to extract. May also be an
* absolute resource path (i.e. starts with "/"), in which case the
* no transformations of the library name are performed. If only the base
@@ -926,7 +1005,7 @@ else if (!Boolean.getBoolean("jna.nounpack")) {
public static native int getLastError();
/** Set the OS last error code. The value will be saved on a per-thread
- * basis.
+ * basis.
*/
public static native void setLastError(int code);
@@ -951,6 +1030,7 @@ public static Library synchronizedLibrary(final Library library) {
}
final Library.Handler handler = (Library.Handler)ih;
InvocationHandler newHandler = new InvocationHandler() {
+ @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
synchronized(handler.getNativeLibrary()) {
return handler.invoke(library, method, args);
@@ -984,6 +1064,7 @@ public static String getWebStartLibraryPath(final String libName) {
final ClassLoader cl = Native.class.getClassLoader();
Method m = (Method)AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
public Object run() {
try {
Method m = ClassLoader.class.getDeclaredMethod("findLibrary", new Class[] { String.class });
@@ -1052,6 +1133,7 @@ static File getTempDir() throws IOException {
static void removeTemporaryFiles() throws IOException {
File dir = getTempDir();
FilenameFilter filter = new FilenameFilter() {
+ @Override
public boolean accept(File dir, String name) {
return name.endsWith(".x") && name.startsWith(JNA_TMPLIB_PREFIX);
}
@@ -1200,6 +1282,7 @@ static Class findDirectMappedClass(Class cls) {
*/
static Class getCallingClass() {
Class[] context = new SecurityManager() {
+ @Override
public Class[] getClassContext() {
return super.getClassContext();
}
@@ -1397,7 +1480,7 @@ private static int getConversion(Class type, TypeMapper mapper) {
* library load path or jna.library.path
.
* @param cls Class with native methods to register
* @param libName name of or path to native library to which functions
- * should be bound
+ * should be bound
*/
public static void register(Class cls, String libName) {
Map options = new HashMap();
@@ -1933,8 +2016,9 @@ static Pointer getTerminationFlag(Thread t) {
private static Map nativeThreads = Collections.synchronizedMap(new WeakHashMap());
- private static ThreadLocal nativeThreadTerminationFlag =
+ private static ThreadLocal nativeThreadTerminationFlag =
new ThreadLocal() {
+ @Override
protected Object initialValue() {
Memory m = new Memory(4);
m.clear();
diff --git a/test/com/sun/jna/NativeTest.java b/test/com/sun/jna/NativeTest.java
index 075e0e5d21..d1252e052e 100644
--- a/test/com/sun/jna/NativeTest.java
+++ b/test/com/sun/jna/NativeTest.java
@@ -82,6 +82,23 @@ public void testCustomStringEncoding() throws Exception {
UNICODE, Native.toString(UNICODEZ.getBytes(ENCODING), ENCODING));
}
+ public void testToStringList() {
+ List expected = Arrays.asList(getClass().getPackage().getName(), getClass().getSimpleName(), "testToStringList");
+ StringBuilder sb = new StringBuilder();
+ for (String value : expected) {
+ sb.append(value).append('\0');
+ }
+ sb.append('\0');
+
+ List actual = Native.toStringList(sb.toString().toCharArray());
+ assertEquals("Mismatched result size", expected.size(), actual.size());
+ for (int index = 0; index < expected.size(); index++) {
+ String expValue = expected.get(index);
+ String actValue = actual.get(index);
+ assertEquals("Mismatched value at index #" + index, expValue, actValue);
+ }
+ }
+
public void testDefaultStringEncoding() throws Exception {
final String UNICODE = "\u0444\u043b\u0441\u0432\u0443";
final String UNICODEZ = UNICODE + "\0more stuff";