java - 如何访问 WMI 查询的数据(通过 JNA)SAFEARRAY 结果
问题描述
我使用 jna 来运行 WMI 查询。以下代码查询 WMI SELECT Caption,Capabilities from Win32_DiskDrive
。Win32_DiskDrive.Capabilities 的类型是 uint16[] 并且 result.getValue 返回一个 SAFEARRAY 实例。
System.out.println("Var Type(3 expected): " + value.getVarType().intValue());
如果我多次启动该过程,则随机返回 0 或 3。
System.out.println("Size (>0 expected): " + (value.getUBound(0) - value.getLBound(0)));
是正确的,但是
Object el = value.getElement(0);
失败。
value.accessData();
返回 null 这也是意外的,所以我不能使用 OaIdlUtil#toPrimitiveArray (Nullpointer)
不幸的是,代码不起作用,我不知道可能出了什么问题。有任何想法吗?
enum Win32_DiskDrive_Values {
Caption,
Capabilities
}
public static void main(String[] args) throws IOException, InterruptedException {
try {
WmiQuery<Win32_DiskDrive_Values> serialNumberQuery = new WmiQuery<Win32_DiskDrive_Values>("Win32_DiskDrive", Win32_DiskDrive_Values.class);
Ole32.INSTANCE.CoInitializeEx(null, Ole32.COINIT_MULTITHREADED);
WmiResult<Win32_DiskDrive_Values> result = serialNumberQuery.execute();
for (int i = 0; i < result.getResultCount(); i++) {
System.out.println(result.getValue(Win32_DiskDrive_Values.Caption, i));
SAFEARRAY value = (SAFEARRAY) result.getValue(Win32_DiskDrive_Values.Capabilities, i);
// According to https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-diskdrive, the type of Capabilities
// should be uint16[] which should be Variant.VT_I2 (2-byte integer)
// however, it is not constant. sometimes it is 0, sometimes Variant.VT_I2 (3);
System.out.println("Var Type(3 expected): " + value.getVarType().intValue());
System.out.println("Size (>0 expected): " + (value.getUBound(0) - value.getLBound(0)));
Object el = value.getElement(0);
System.out.println("Element 0 (!=null expected): " + el);
Pointer pointer = value.accessData();
System.out.println("pointer (!=null expected): " + pointer);
}
} catch (Throwable e) {
e.printStackTrace();
} finally {
Ole32.INSTANCE.CoUninitialize();
}
}
解决方案
我提交给 JNA 项目的 WMI 代码仅用于处理原始值和字符串,而不是数组。您遇到的问题是 WMI 将指针地址返回到数组(VT_EMPTY = 0 的空数组或 VT_I4 = 3 的 32 位指针)。但是 WMI 结果是在迭代之后释放的,所以不能使用WmiResult
来获取对象。
您需要编写自己的代码(使用 JNA 实现作为起点)来获取SAFEARRAY
迭代期间的内容。您在 JNA 网站上提出了这个问题,@matthiasblaesing 发布了以下适用于您的用例的片段:
public static void main(String[] args) throws IOException, InterruptedException {
Ole32.INSTANCE.CoInitializeEx(null, Ole32.COINIT_MULTITHREADED);
// Connect to the server
Wbemcli.IWbemServices svc = WbemcliUtil.connectServer("ROOT\\CIMV2");
// Send query
try {
Wbemcli.IEnumWbemClassObject enumerator = svc.ExecQuery("WQL", "SELECT Caption, Capabilities, CapabilityDescriptions FROM Win32_DiskDrive",
Wbemcli.WBEM_FLAG_FORWARD_ONLY | Wbemcli.WBEM_FLAG_RETURN_IMMEDIATELY, null);
try {
IWbemClassObject[] result;
VARIANT.ByReference pVal = new VARIANT.ByReference();
IntByReference pType = new IntByReference();
IntByReference plFlavor = new IntByReference();
while(true) {
result = enumerator.Next(0, 1);
if(result.length == 0) {
break;
}
COMUtils.checkRC(result[0].Get("Caption", 0, pVal, pType, plFlavor));
System.out.println("---------" + pVal.getValue() + "-------------");
OleAuto.INSTANCE.VariantClear(pVal);
COMUtils.checkRC(result[0].Get("CapabilityDescriptions", 0, pVal, pType, plFlavor));
SAFEARRAY safeArray = (SAFEARRAY) pVal.getValue();
for(int i = safeArray.getLBound(0); i<=safeArray.getUBound(0); i++) {
System.out.println("\t" + safeArray.getElement(i));
}
OleAuto.INSTANCE.VariantClear(pVal);
COMUtils.checkRC(result[0].Get("Capabilities", 0, pVal, pType, plFlavor));
safeArray = (SAFEARRAY) pVal.getValue();
for(int i = safeArray.getLBound(0); i<=safeArray.getUBound(0); i++) {
System.out.println("\t" + safeArray.getElement(i));
}
OleAuto.INSTANCE.VariantClear(pVal);
result[0].Release();
}
} finally {
// Cleanup
enumerator.Release();
}
} finally {
// Cleanup
svc.Release();
}
Ole32.INSTANCE.CoUninitialize();
}
推荐阅读
- jquery - "expression expected" error
- excel - 子集的时间长度
- php - 无法加载动态库“php_wincache.dll”?
- javascript - AngularJS - 添加功能
- ios - 如何在 ios swift 中获取 emoji 的 unicode
- gcc - gfortran 是否可以执行链接时间优化,从而导致内联来自不同翻译单元的纯函数?
- ios - FontAwesome 图标显示为问号
- node.js - 如何在使用 mongodb 查找多个集合后从集合中匹配字符串
- vba - 将数据透视表数据获取到用户窗体 VBA
- regex - 使用 RegEx 捕获重复组