我已经成功使用JNA来调用几个Windows API函数,但我遇到了这个问题
GetVolumePathNamesForVolumeName
完整的C声明是:
BOOL WINAPI GetVolumePathNamesForVolumeName(
__in LPCTSTR lpszVolumeName,
__out LPTSTR lpszVolumePathNames,
__in DWORD cchBufferLength,
__out PDWORD lpcchReturnLength
);
我的Kernel32接口方法原型是:
boolean GetVolumePathNamesForVolumeName(String lpszVolumeName, Pointer lpszVolumePathNames, int cchBufferLength, Pointer lpcchReturnLength);
我使用以下内容加载界面
Native.loadLibrary('kernel32', Kernel32.class, W32APIOptions.UNICODE_OPTIONS)
我试过了:
public String[] getPathNames() {
Memory pathNames = new Memory(100);
Memory len = new Memory(4);
if (!kernel32.GetVolumePathNamesForVolumeName(this.getGuidPath(), pathNames, 100, len)) {
if (kernel32.GetLastError() == WindowsConstants.ERROR_MORE_DATA) {
pathNames = new Memory(len.getInt(0));
if (!kernel32.GetVolumePathNamesForVolumeName(this.getGuidPath(), pathNames, len.getInt(0), len)) {
throw new WinApiException(kernel32.GetLastError());
}
}
else
throw new WinApiException(kernel32.GetLastError());
}
int count = len.getInt(0);
return pathNames.getStringArray(0, true);
}
根本不起作用。不确定异常,但我的代码爆炸了。
以下是有效的作品:
public String[] getPathNames() {
Memory pathNames = new Memory(100);
Memory len = new Memory(4);
if (!kernel32.GetVolumePathNamesForVolumeName(this.getGuidPath(), pathNames, 100, len)) {
if (kernel32.GetLastError() == WindowsConstants.ERROR_MORE_DATA) {
pathNames = new Memory(len.getInt(0));
if (!kernel32.GetVolumePathNamesForVolumeName(this.getGuidPath(), pathNames, len.getInt(0), len)) {
throw new WinApiException(kernel32.GetLastError());
}
}
else
throw new WinApiException(kernel32.GetLastError());
}
int count = len.getInt(0);
String[] result = new String[count];
int offset = 0;
for (int i = 0; i < count; i++) {
result[i] = pathNames.getString(offset, true);
offset += result[i].length() * Native.WCHAR_SIZE + Native.WCHAR_SIZE;
}
return result;
}
这个会发生什么,第一个值很好,但之后可以看到存在编码问题,表明我的错误是错误的。
答案 0 :(得分:5)
我的版本\\?\Volume{5b57f944-8d60-11de-8b2a-806d6172696f}\
应获得C:\
Kernel32接口:
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();
}
测试应用程序:
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;
}
}
结果:
[C, :, \, , ]
要仔细检查,我在DOS命令提示符下键入:mountvol
已编辑:要改善结果值...
更改getPathNames()
方法的返回值,来自:
return Arrays.toString(pathNames);
到
return new String(pathNames);
在我的测试应用程序中,您可以:
String[] points = getPathNames().split("\u0000"); //split by Unicode NULL
for(String s: points) System.out.println("mount: " + s);
我唯一担心的是JNA如何处理来自Kernel32 lpszVolumePathNames
方法中GetVolumePathNamesForVolumeNameW()
参数的以NULL结尾的Unicode字符串,因为:
lpszVolumePathNames [out]
指向接收缓冲区的指针 驱动器号和卷的列表 GUID路径。该列表是一个数组 以null结尾的字符串终止 一个额外的NULL字符。如果 缓冲区不够大,无法容纳 完整列表,缓冲区保持为 列表的大部分内容。
虽然,JNI规范说(我不确定JNA方面的事情):
10.8终止Unicode字符串
从中获取的Unicode字符串 GetStringChars或GetStringCritical 不是以NULL结尾的。呼叫 GetStringLength找出数字 一个16位的Unicode字符 串。一些操作系统,如 作为Windows NT,期待两个尾随 零字节值以终止Unicode 字符串。你无法传递结果 GetStringChars到Windows NT API 期待一个Unicode字符串。你必须做 字符串和插入的另一个副本 两个尾随的零字节值。
<强>编辑:强>
似乎我的代码没问题,因为lpszVolumePathNames
参数通过验证其中是否存在“\ u0000”字符串,正确地在Unicode中返回以NULL结尾的字符串:
String point = getPathNames().replaceAll("\u0000", "-");
答案 1 :(得分:1)
感谢eee的回答。它引导我进行以下操作。你回答差不多完成了。只需要最后一点将结果char []拆分为由空字符分隔的路径组件。
// 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;
}
答案 2 :(得分:0)
使用我的版本:
更改getPathNames()
方法的返回值,来自:
return Arrays.toString(pathNames);
到
return new String(pathNames);
在我的测试应用程序中,您可以:
String[] points = getPathNames().split("\u0000"); //split by Unicode NULL
for(String s: points) System.out.println("mount: " + s);
已编辑:此帖子将更新到我之前的帖子