I\'ve successfully used JNA to call a couple of Windows API functions but I get stuck at this one
GetVolumePathNamesForVolumeName
The full C declaration is:<
Thanks eee for your answer. It led me to the following. You answer was almost complete. Just needed a last bit to split the resultant char[] into the path components which is separated by null characters.
// Decleration...
public interface Kernel32 extends StdCallLibrary {
public boolean GetVolumePathNamesForVolumeName(
WString lpszVolumeName,
char[] lpszVolumePathNames,
int cchBufferLength,
IntByReference lpcchReturnLength
);
// Other methods....
}
...
// Instantiation
Native.loadLibrary('kernel32', Kernel32.class, W32APIOptions.UNICODE_OPTIONS)
...
// Usage
public List<String> getMountPoints() {
char[] pathNames = new char[100];
IntByReference len = new IntByReference();
if (!kernel32.GetVolumePathNamesForVolumeName(new WString(this.getGuidPath()), pathNames, 100, len)) {
if (kernel32.GetLastError() == WindowsConstants.ERROR_MORE_DATA) {
pathNames = new char[len.getValue()];
if (!kernel32.GetVolumePathNamesForVolumeName(new WString(this.getGuidPath()), pathNames, len.getValue(), len)) {
throw new WinApiException(kernel32.GetLastError());
}
}
else
throw new WinApiException(kernel32.GetLastError());
}
List<String> list = new LinkedList<String>();
int offset = 0;
for (int i = 0; i < pathNames.length; i++) {
if (pathNames[i] == '\u0000') {
list.add(String.valueOf(pathNames, offset, i-offset));
offset = i+1;
if (pathNames[i+1] == '\u0000')
break;
}
}
return list;
}
Using my version:
Change the return value of getPathNames()
method, from:
return Arrays.toString(pathNames);
to
return new String(pathNames);
In my test application, you can just:
String[] points = getPathNames().split("\u0000"); //split by Unicode NULL
for(String s: points) System.out.println("mount: " + s);
Edited: this post will be updated into my previous post
My version \\?\Volume{5b57f944-8d60-11de-8b2a-806d6172696f}\
should get C:\
Kernel32 Interface:
import com.sun.jna.WString;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.ptr.IntByReference;
import com.sun.jna.win32.StdCallLibrary;
public interface Kernel32 extends StdCallLibrary {
public boolean GetVolumePathNamesForVolumeNameW(
WString lpszVolumeName,
char[] lpszVolumePathNames,
DWORD cchBufferLength,
IntByReference lpcchReturnLength
);
public int GetLastError();
}
Test application:
import java.util.Arrays;
import com.sun.jna.Native;
import com.sun.jna.WString;
import com.sun.jna.platform.win32.Win32Exception;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.ptr.IntByReference;
public class TestJNA {
static Kernel32 kernel32 = (Kernel32) Native.loadLibrary("kernel32.dll", Kernel32.class);
/**
* @param args
*/
public static void main(String[] args) {
try {
System.out.println(getPathNames());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static String getPathNames() throws Win32Exception {
DWORD value = new DWORD(100);
char[] pathNames = new char[100];
IntByReference len = new IntByReference();
if (kernel32.GetVolumePathNamesForVolumeNameW(getGuidPath(), pathNames, value, len)) {
if (kernel32.GetLastError() == WindowsConstants.ERROR_MORE_DATA) {
pathNames = new char[len.getValue()];
DWORD sz = new DWORD(len.getValue());
if (!kernel32.GetVolumePathNamesForVolumeNameW(getGuidPath(), pathNames, sz, len)) {
throw new Win32Exception(kernel32.GetLastError());
}
}
else
throw new Win32Exception(kernel32.GetLastError());
}
return Arrays.toString(pathNames);
}
private static WString getGuidPath() {
final WString str = new WString("\\\\?\\Volume{5b57f944-8d60-11de-8b2a-806d6172696f}\\");
return str;
}
}
Result:
[C, :, \, , ]
To double-check it, I type at DOS command prompt: mountvol
Edited: To improve of the result value...
Change the return value of getPathNames()
method, from:
return Arrays.toString(pathNames);
to
return new String(pathNames);
In my test application, you can just:
String[] points = getPathNames().split("\u0000"); //split by Unicode NULL
for(String s: points) System.out.println("mount: " + s);
My only concern is how JNA handles NULL-terminated Unicode strings from lpszVolumePathNames
parameter in Kernel32 GetVolumePathNamesForVolumeNameW()
method since:
lpszVolumePathNames [out]
A pointer to a buffer that receives the list of drive letters and volume GUID 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.
Though, JNI specification says (I am not sure on JNA side of thing):
10.8 Terminating Unicode Strings
Unicode strings obtained from GetStringChars or GetStringCritical are not NULL-terminated. Call GetStringLength to find out the number of 16-bit Unicode characters in a string. Some operating systems, such as Windows NT, expect two trailing zero byte values to terminate Unicode strings. You cannot pass the result of GetStringChars to Windows NT APIs that expect a Unicode string. You must make another copy of the string and insert the two trailing zero byte values.
http://java.sun.com/docs/books/jni/html/pitfalls.html
Edited:
It seems that my code is OK as the lpszVolumePathNames
parameter returns NULL-terminated strings in Unicode correctly by verifying the presence of "\u0000" strings within it:
String point = getPathNames().replaceAll("\u0000", "-");