diff --git a/CHANGES.md b/CHANGES.md index bfa1bba0d2..3d04c6d91a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -32,6 +32,7 @@ Features * [#1001](https://github.com/java-native-access/jna/pull/1001): Added overloads to `c.s.j.platform.win32.Advapi32Util` methods which allow the caller to specify `samDesiredExtra` to request additional registry key security and access rights - [@camw](https://github.com/camw). * [#1007](https://github.com/java-native-access/jna/pull/1007): Added OSGi export of Solaris package - [@swimmesberger](https://github.com/swimmesberger). * [#1003](https://github.com/java-native-access/jna/pull/1003): Allow `NativeMapped` to be used with enums - [@koraktor](https://github.com/koraktor). +* [#994](https://github.com/java-native-access/jna/issues/994): Added `CoInitializeSecurity` and `CoSetProxyBlanket` to `c.s.j.platform.win32.Ole32`, added new `c.s.j.platform.win32.Wbemcli` classes needed to query WMI, and added a `WbemcliUtil` class implementing WMI queries. - [@dbwiddis](https://github.com/dbwiddis). Bug Fixes --------- diff --git a/contrib/platform/src/com/sun/jna/platform/win32/COM/Wbemcli.java b/contrib/platform/src/com/sun/jna/platform/win32/COM/Wbemcli.java new file mode 100644 index 0000000000..7ca5199605 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/win32/COM/Wbemcli.java @@ -0,0 +1,255 @@ +/* Copyright (c) 2018 Daniel Widdis, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.win32.COM; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Guid.CLSID; +import com.sun.jna.platform.win32.Guid.GUID; +import com.sun.jna.platform.win32.Ole32; +import com.sun.jna.platform.win32.OleAuto; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WTypes; +import com.sun.jna.platform.win32.WTypes.BSTR; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; + +/** + * This header is used by Remote Desktop Services. It contains programming + * interfaces for enumerating and querying Common Information Model (CIM) + * objects. + */ +public interface Wbemcli { + + public static final int WBEM_FLAG_RETURN_IMMEDIATELY = 0x00000010; + public static final int WBEM_FLAG_FORWARD_ONLY = 0x00000020; + public static final int WBEM_INFINITE = 0xFFFFFFFF; + + // Non-error constants + // https://docs.microsoft.com/en-us/windows/desktop/wmisdk/wmi-non-error-constants + public static final int WBEM_S_NO_ERROR = 0x0; + public static final int WBEM_S_FALSE = 0x1; + public static final int WBEM_S_TIMEDOUT = 0x40004; + public static final int WBEM_S_NO_MORE_DATA = 0x40005; + + // Error constants + // https://docs.microsoft.com/en-us/windows/desktop/wmisdk/wmi-error-constants + public static final int WBEM_E_INVALID_NAMESPACE = 0x8004100e; + public static final int WBEM_E_INVALID_CLASS = 0x80041010; + public static final int WBEM_E_INVALID_QUERY = 0x80041017; + + // CIM Types + public static final int CIM_ILLEGAL = 0xfff; + public static final int CIM_EMPTY = 0; + public static final int CIM_SINT8 = 16; + public static final int CIM_UINT8 = 17; + public static final int CIM_SINT16 = 2; + public static final int CIM_UINT16 = 18; + public static final int CIM_SINT32 = 3; + public static final int CIM_UINT32 = 19; + public static final int CIM_SINT64 = 20; + public static final int CIM_UINT64 = 21; + public static final int CIM_REAL32 = 4; + public static final int CIM_REAL64 = 5; + public static final int CIM_BOOLEAN = 11; + public static final int CIM_STRING = 8; + public static final int CIM_DATETIME = 101; + public static final int CIM_REFERENCE = 102; + public static final int CIM_CHAR16 = 103; + public static final int CIM_OBJECT = 13; + public static final int CIM_FLAG_ARRAY = 0x2000; + + /** + * Contains and manipulates both WMI class definitions and class object + * instances. + */ + class IWbemClassObject extends Unknown { + + public IWbemClassObject() { + } + + public IWbemClassObject(Pointer pvInstance) { + super(pvInstance); + } + + public HRESULT Get(WString wszName, int lFlags, VARIANT.ByReference pVal, IntByReference pType, + IntByReference plFlavor) { + // Get is 5th method of IWbemClassObjectVtbl in WbemCli.h + return (HRESULT) _invokeNativeObject(4, + new Object[] { getPointer(), wszName, lFlags, pVal, pType, plFlavor }, HRESULT.class); + } + + public HRESULT Get(String wszName, int lFlags, VARIANT.ByReference pVal, IntByReference pType, + IntByReference plFlavor) { + return Get(wszName == null ? null : new WString(wszName), lFlags, pVal, pType, plFlavor); + } + } + + /** + * Used to enumerate Common Information Model (CIM) objects. + */ + class IEnumWbemClassObject extends Unknown { + + public IEnumWbemClassObject() { + } + + public IEnumWbemClassObject(Pointer pvInstance) { + super(pvInstance); + } + + public HRESULT Next(int lTimeOut, int uCount, Pointer[] ppObjects, IntByReference puReturned) { + // Next is 5th method of IEnumWbemClassObjectVtbl in + // WbemCli.h + return (HRESULT) _invokeNativeObject(4, + new Object[] { getPointer(), lTimeOut, uCount, ppObjects, puReturned }, HRESULT.class); + } + + public IWbemClassObject[] Next(int lTimeOut, int uCount) { + Pointer[] resultArray = new Pointer[uCount]; + IntByReference resultCount = new IntByReference(); + HRESULT result = Next(lTimeOut, uCount, resultArray, resultCount); + COMUtils.checkRC(result); + IWbemClassObject[] returnValue = new IWbemClassObject[resultCount.getValue()]; + for (int i = 0; i < resultCount.getValue(); i++) { + returnValue[i] = new IWbemClassObject(resultArray[i]); + } + return returnValue; + } + } + + /** + * Used to obtain the initial namespace pointer to the IWbemServices + * interface for WMI on a specific host computer. + */ + class IWbemLocator extends Unknown { + + public static final CLSID CLSID_WbemLocator = new CLSID("4590f811-1d3a-11d0-891f-00aa004b2e24"); + public static final GUID IID_IWbemLocator = new GUID("dc12a687-737f-11cf-884d-00aa004b2e24"); + + public IWbemLocator() { + } + + private IWbemLocator(Pointer pvInstance) { + super(pvInstance); + } + + public static IWbemLocator create() { + PointerByReference pbr = new PointerByReference(); + + HRESULT hres = Ole32.INSTANCE.CoCreateInstance(CLSID_WbemLocator, null, WTypes.CLSCTX_INPROC_SERVER, + IID_IWbemLocator, pbr); + if (COMUtils.FAILED(hres)) { + return null; + } + + return new IWbemLocator(pbr.getValue()); + } + + public HRESULT ConnectServer(BSTR strNetworkResource, BSTR strUser, BSTR strPassword, BSTR strLocale, + int lSecurityFlags, BSTR strAuthority, IWbemContext pCtx, PointerByReference ppNamespace) { + // ConnectServier is 4th method of IWbemLocatorVtbl in WbemCli.h + return (HRESULT) _invokeNativeObject(3, new Object[] { getPointer(), strNetworkResource, strUser, + strPassword, strLocale, lSecurityFlags, strAuthority, pCtx, ppNamespace }, HRESULT.class); + } + + public IWbemServices ConnectServer(String strNetworkResource, String strUser, String strPassword, + String strLocale, int lSecurityFlags, String strAuthority, IWbemContext pCtx) { + BSTR strNetworkResourceBSTR = OleAuto.INSTANCE.SysAllocString(strNetworkResource); + BSTR strUserBSTR = OleAuto.INSTANCE.SysAllocString(strUser); + BSTR strPasswordBSTR = OleAuto.INSTANCE.SysAllocString(strPassword); + BSTR strLocaleBSTR = OleAuto.INSTANCE.SysAllocString(strLocale); + BSTR strAuthorityBSTR = OleAuto.INSTANCE.SysAllocString(strAuthority); + + PointerByReference pbr = new PointerByReference(); + + try { + HRESULT result = ConnectServer(strNetworkResourceBSTR, strUserBSTR, strPasswordBSTR, strLocaleBSTR, + lSecurityFlags, strAuthorityBSTR, pCtx, pbr); + + COMUtils.checkRC(result); + + return new IWbemServices(pbr.getValue()); + } finally { + OleAuto.INSTANCE.SysFreeString(strNetworkResourceBSTR); + OleAuto.INSTANCE.SysFreeString(strUserBSTR); + OleAuto.INSTANCE.SysFreeString(strPasswordBSTR); + OleAuto.INSTANCE.SysFreeString(strLocaleBSTR); + OleAuto.INSTANCE.SysFreeString(strAuthorityBSTR); + } + } + } + + /** + * Used by clients and providers to access WMI services. The interface is + * implemented by WMI and WMI providers, and is the primary WMI interface. + */ + class IWbemServices extends Unknown { + + public IWbemServices() { + } + + public IWbemServices(Pointer pvInstance) { + super(pvInstance); + } + + public HRESULT ExecQuery(BSTR strQueryLanguage, BSTR strQuery, int lFlags, IWbemContext pCtx, + PointerByReference ppEnum) { + // ExecQuery is 21st method of IWbemServicesVtbl in WbemCli.h + return (HRESULT) _invokeNativeObject(20, + new Object[] { getPointer(), strQueryLanguage, strQuery, lFlags, pCtx, ppEnum }, HRESULT.class); + } + + public IEnumWbemClassObject ExecQuery(String strQueryLanguage, String strQuery, int lFlags, IWbemContext pCtx) { + BSTR strQueryLanguageBSTR = OleAuto.INSTANCE.SysAllocString(strQueryLanguage); + BSTR strQueryBSTR = OleAuto.INSTANCE.SysAllocString(strQuery); + try { + PointerByReference pbr = new PointerByReference(); + + HRESULT res = ExecQuery(strQueryLanguageBSTR, strQueryBSTR, lFlags, pCtx, pbr); + + COMUtils.checkRC(res); + + return new IEnumWbemClassObject(pbr.getValue()); + } finally { + OleAuto.INSTANCE.SysFreeString(strQueryLanguageBSTR); + OleAuto.INSTANCE.SysFreeString(strQueryBSTR); + } + } + } + + /** + * Optionally used to communicate additional context information to + * providers when submitting IWbemServices calls to WMI + */ + class IWbemContext extends Unknown { + + public IWbemContext() { + } + + public IWbemContext(Pointer pvInstance) { + super(pvInstance); + } + } +} diff --git a/contrib/platform/src/com/sun/jna/platform/win32/COM/WbemcliUtil.java b/contrib/platform/src/com/sun/jna/platform/win32/COM/WbemcliUtil.java new file mode 100644 index 0000000000..a6727eacd3 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/win32/COM/WbemcliUtil.java @@ -0,0 +1,534 @@ +/* Copyright (c) 2018 Daniel Widdis, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.win32.COM; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeoutException; + +import com.sun.jna.Pointer; +import com.sun.jna.WString; +import com.sun.jna.platform.win32.Ole32; +import com.sun.jna.platform.win32.OleAuto; +import com.sun.jna.platform.win32.Variant; +import com.sun.jna.platform.win32.Variant.VARIANT; +import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.COM.Wbemcli.IEnumWbemClassObject; +import com.sun.jna.platform.win32.COM.Wbemcli.IWbemClassObject; +import com.sun.jna.platform.win32.COM.Wbemcli.IWbemLocator; +import com.sun.jna.platform.win32.COM.Wbemcli.IWbemServices; +import com.sun.jna.ptr.IntByReference; + +/** + * Utility class providing access to Windows Management Interface (WMI) via COM. + */ +public class WbemcliUtil { + + /** + * Instance to generate the WmiQuery class. + */ + public static final WbemcliUtil INSTANCE = new WbemcliUtil(); + + /** + * The default namespace for most WMI queries. + */ + public static final String DEFAULT_NAMESPACE = "ROOT\\CIMV2"; + + /** + * Enum containing the property used for WMI Namespace query. + */ + private enum NamespaceProperty { + NAME; + } + + /** + * Helper class wrapping information required for a WMI query. + */ + public static class WmiQuery> { + + private String nameSpace; + private String wmiClassName; + private Class propertyEnum; + + /** + * Instantiate a WmiQuery. + * + * @param nameSpace + * The WMI namespace to use. + * @param wmiClassName + * The WMI class to use. Optionally include a WQL WHERE + * clause with filters results to properties matching the + * input. + * @param propertyEnum + * An enum for type mapping. + */ + public WmiQuery(String nameSpace, String wmiClassName, Class propertyEnum) { + super(); + this.nameSpace = nameSpace; + this.wmiClassName = wmiClassName; + this.propertyEnum = propertyEnum; + } + + /** + * Instantiate a WMI Query in the default namespace + * + * @param wmiClassName The WMI Class to use. May include a WHERE clause + * with filtering conditions. + * @param propertyEnum An Enum that contains the properties to query + */ + public WmiQuery(String wmiClassName, Class propertyEnum) { + this(DEFAULT_NAMESPACE, wmiClassName, propertyEnum); + } + + /** + * @return The enum containing the properties + */ + public Class getPropertyEnum() { + return propertyEnum; + } + + /** + * @return The namespace + */ + public String getNameSpace() { + return nameSpace; + } + + /** + * @param nameSpace + * The namespace to set + */ + public void setNameSpace(String nameSpace) { + this.nameSpace = nameSpace; + } + + /** + * @return The class name + */ + public String getWmiClassName() { + return wmiClassName; + } + + /** + * @param wmiClassName + * The classname to set + */ + public void setWmiClassName(String wmiClassName) { + this.wmiClassName = wmiClassName; + } + + /** + * Query WMI for values, with no timeout. + * + * @return a WmiResult object containing the query results, wrapping an + * EnumMap + */ + public WmiResult execute() { + try { + return execute(Wbemcli.WBEM_INFINITE); + } catch (TimeoutException e) { + throw new COMException("Got a WMI timeout when infinite wait was specified. This should never happen."); + } + } + + /** + * Query WMI for values, with a specified timeout. + * + * @param timeout + * Number of milliseconds to wait for results before timing + * out. If {@link IEnumWbemClassObject#WBEM_INFINITE} (-1), + * will always wait for results. If a timeout occurs, throws + * a {@link TimeoutException}. + * + * @return a WmiResult object containing the query results, wrapping an + * EnumMap + * + * @throws TimeoutException + * if the query times out before completion + */ + public WmiResult execute(int timeout) throws TimeoutException { + // Idiot check + if (getPropertyEnum().getEnumConstants().length < 1) { + throw new IllegalArgumentException("The query's property enum has no values."); + } + + // Connect to the server + IWbemServices svc = connectServer(getNameSpace()); + + // Send query + try { + IEnumWbemClassObject enumerator = selectProperties(svc, this); + + try { + return enumerateProperties(enumerator, getPropertyEnum(), timeout); + } finally { + // Cleanup + enumerator.Release(); + } + } finally { + // Cleanup + svc.Release(); + } + + } + + /** + * Selects properties from WMI. Returns immediately (asynchronously), + * even while results are being retrieved; results may begun to be + * enumerated in the forward direction only. + * + * @param svc + * A WbemServices object to make the calls + * @param query + * A WmiQuery object encapsulating the details of the query + * + * @return An enumerator to receive the results of the query + */ + private static > IEnumWbemClassObject selectProperties(IWbemServices svc, WmiQuery query) { + // Step 6: -------------------------------------------------- + // Use the IWbemServices pointer to make requests of WMI ---- + T[] props = query.getPropertyEnum().getEnumConstants(); + StringBuilder sb = new StringBuilder("SELECT "); + // We earlier checked for at least one enum constant + sb.append(props[0].name()); + for (int i = 1; i < props.length; i++) { + sb.append(',').append(props[i].name()); + } + sb.append(" FROM ").append(query.getWmiClassName()); + // Send the query. The flags allow us to return immediately and begin + // enumerating in the forward direction as results come in. + return svc.ExecQuery("WQL", sb.toString().replaceAll("\\\\", "\\\\\\\\"), + Wbemcli.WBEM_FLAG_FORWARD_ONLY | Wbemcli.WBEM_FLAG_RETURN_IMMEDIATELY, null); + } + + /*- + * The following table maps WMI return types (CIM type) to the VT type of + * the returned VARIANT. + * + * CIM type | VT type + * ----------|---------- + * BOOLEAN | VT_BOOL + * ----------|---------- + * UINT8 | VT_UI1 + * ----------|---------- + * SINT8 | VT_I2 + * SINT16 | VT_I2 + * CHAR16 | VT_I2 + * ----------|---------- + * UINT16 | VT_I4 + * SINT32 | VT_I4 + * UINT32 | VT_I4 + * ----------|---------- + * SINT64 | VT_BSTR + * UINT64 | VT_BSTR + * DATETIME | VT_BSTR + * REFERENCE | VT_BSTR + * STRING | VT_BSTR + * ----------|---------- + * REAL32 | VT_R4 + * ----------|---------- + * REAL64 | VT_R8 + * ----------|---------- + * OBJECT | VT_UNKNOWN (not implemented) + */ + /** + * Enumerate the results of a WMI query. This method is called while + * results are still being retrieved and may iterate in the forward + * direction only. + * + * @param enumerator + * The enumerator with the results + * @param propertyEnum + * The enum containing the properties to enumerate, which are + * the keys to the WmiResult map + * @param timeout + * Number of milliseconds to wait for results before timing + * out. If {@link IEnumWbemClassObject#WBEM_INFINITE} (-1), + * will always wait for results. + * + * @return A WmiResult object encapsulating an EnumMap which will hold + * the results. + * + * @throws TimeoutException + * if the query times out before completion + */ + private static > WmiResult enumerateProperties(IEnumWbemClassObject enumerator, + Class propertyEnum, int timeout) throws TimeoutException { + WmiResult values = INSTANCE.new WmiResult(propertyEnum); + // Step 7: ------------------------------------------------- + // Get the data from the query in step 6 ------------------- + Pointer[] pclsObj = new Pointer[1]; + IntByReference uReturn = new IntByReference(0); + Map wstrMap = new HashMap(); + HRESULT hres = null; + for (T property : propertyEnum.getEnumConstants()) { + wstrMap.put(property, new WString(property.name())); + } + while (enumerator.getPointer() != Pointer.NULL) { + // Enumerator will be released by calling method so no need to + // release it here. + hres = enumerator.Next(timeout, pclsObj.length, pclsObj, uReturn); + // Enumeration complete or no more data; we're done, exit the loop + if (hres.intValue() == Wbemcli.WBEM_S_FALSE || hres.intValue() == Wbemcli.WBEM_S_NO_MORE_DATA) { + break; + } + // Throw exception to notify user of timeout + if (hres.intValue() == Wbemcli.WBEM_S_TIMEDOUT) { + throw new TimeoutException("No results after " + timeout + " ms."); + } + // Other exceptions here. + if (COMUtils.FAILED(hres)) { + throw new COMException("Failed to enumerate results.", hres); + } + + VARIANT.ByReference pVal = new VARIANT.ByReference(); + IntByReference pType = new IntByReference(); + + // Get the value of the properties + IWbemClassObject clsObj = new IWbemClassObject(pclsObj[0]); + for (T property : propertyEnum.getEnumConstants()) { + clsObj.Get(wstrMap.get(property), 0, pVal, pType, null); + int vtType = (pVal.getValue() == null ? Variant.VT_NULL : pVal.getVarType()).intValue(); + int cimType = pType.getValue(); + switch (vtType) { + case Variant.VT_BSTR: + values.add(vtType, cimType, property, pVal.stringValue()); + break; + case Variant.VT_I4: + values.add(vtType, cimType, property, pVal.intValue()); + break; + case Variant.VT_UI1: + values.add(vtType, cimType, property, pVal.byteValue()); + break; + case Variant.VT_I2: + values.add(vtType, cimType, property, pVal.shortValue()); + break; + case Variant.VT_BOOL: + values.add(vtType, cimType, property, pVal.booleanValue()); + break; + case Variant.VT_R4: + values.add(vtType, cimType, property, pVal.floatValue()); + break; + case Variant.VT_R8: + values.add(vtType, cimType, property, pVal.doubleValue()); + break; + case Variant.VT_NULL: + values.add(vtType, cimType, property, null); + break; + // Unimplemented type. User must cast + default: + values.add(vtType, cimType, property, pVal.getValue()); + } + OleAuto.INSTANCE.VariantClear(pVal); + } + clsObj.Release(); + + values.incrementResultCount(); + } + return values; + } + } + + /** + * Helper class wrapping an EnumMap containing the results of a query. + */ + public class WmiResult> { + private Map> propertyMap; + private Map vtTypeMap; + private Map cimTypeMap; + private int resultCount = 0; + + /** + * @param propertyEnum + * The enum associated with this map + */ + public WmiResult(Class propertyEnum) { + propertyMap = new EnumMap>(propertyEnum); + vtTypeMap = new EnumMap(propertyEnum); + cimTypeMap = new EnumMap(propertyEnum); + for (T prop : propertyEnum.getEnumConstants()) { + propertyMap.put(prop, new ArrayList()); + vtTypeMap.put(prop, Variant.VT_NULL); + cimTypeMap.put(prop, Wbemcli.CIM_EMPTY); + } + } + + /** + * Gets a value from the WmiResult, which may be null. User must check + * for null and cast the result. Types correlate to the CIM Type of the + * enumerated WMI property and will be consistent for a given property, + * and may be validated by the user using {@link #getVtType} or the + * Class of the returned Object. + * + * @param property + * The property (column) to fetch + * @param index + * The index (row) to fetch + * @return The Object containing the specified value, which may be null + */ + public Object getValue(T property, int index) { + return this.propertyMap.get(property).get(index); + } + + /** + * Gets the Variant type from the WmiResult. The integer value is + * defined as a VT_* constant in the + * {@link com.sun.jna.platform.win32.Variant} interface. + * + * @param property + * The property (column) whose type to fetch + * @return An integer representing the Variant type + */ + public int getVtType(T property) { + return this.vtTypeMap.get(property); + } + + /** + * Gets the CIM type from the WmiResult. The integer value is defined as + * a CIM_* constant in the {@link Wbemcli} interface. + * + * @param property + * The property (column) whose type to fetch + * @return An integer representing the CIM type + */ + public int getCIMType(T property) { + return this.cimTypeMap.get(property); + } + + /** + * Adds a value to the WmiResult at the next index for that property + * + * @param vtType + * The Variant type of this object + * @param cimType + * The CIM type of this property + * @param property + * The property (column) to store + * @param o + * The object to store + */ + private void add(int vtType, int cimType, T property, Object o) { + this.propertyMap.get(property).add(o); + if (vtType != Variant.VT_NULL && this.vtTypeMap.get(property).equals(Variant.VT_NULL)) { + this.vtTypeMap.put(property, vtType); + } + if (this.cimTypeMap.get(property).equals(Wbemcli.CIM_EMPTY)) { + this.cimTypeMap.put(property, cimType); + } + } + + /** + * @return The number of results in each mapped list + */ + public int getResultCount() { + return this.resultCount; + } + + /** + * Increment the result count by one. + */ + private void incrementResultCount() { + this.resultCount++; + } + } + + + /** + * Determine if WMI has the requested namespace. Some namespaces only exist + * on newer versions of Windows. + * + * @param namespace + * The namespace to test + * @return true if the namespace exists, false otherwise + */ + public static boolean hasNamespace(String namespace) { + // Strip off leading ROOT\ for valid match + String ns = namespace; + if (namespace.toUpperCase().startsWith("ROOT\\")) { + ns = namespace.substring(5); + } + // Test + WmiQuery namespaceQuery = new WmiQuery("ROOT", "__NAMESPACE", NamespaceProperty.class); + WmiResult namespaces = namespaceQuery.execute(); + for (int i = 0; i < namespaces.getResultCount(); i++) { + if (ns.equalsIgnoreCase((String) namespaces.getValue(NamespaceProperty.NAME, i))) { + return true; + } + } + return false; + } + + /* + * Below methods ported from: Getting WMI Data from Local Computer + * https://docs.microsoft.com/en-us/windows/desktop/WmiSdk/example--getting- + * wmi-data-from-the-local-computer + * + * Steps in the comments correspond to the above link. Steps 1 - 2 are the + * responsibility of the user. Steps 3 - 5 contain all the steps required to + * set up and connect to WMI, and steps 6 and 7 are where data is queried + * and received. + */ + + /** + * Obtains a locator to the WMI server and connects to the specified + * namespace + * + * @param namespace + * The namespace to connect to + * @return A service representing the connected namespace, which can be + * queried. This service may be re-used for multiple queries and + * should be released by the user + */ + public static IWbemServices connectServer(String namespace) { + // Step 3: --------------------------------------------------- + // Obtain the initial locator to WMI ------------------------- + IWbemLocator loc = IWbemLocator.create(); + if (loc == null) { + throw new COMException("Failed to create WbemLocator object."); + } + + // Step 4: ----------------------------------------------------- + // Connect to WMI through the IWbemLocator::ConnectServer method + // Connect to the namespace with the current user and obtain pointer + // pSvc to make IWbemServices calls. + IWbemServices services = loc.ConnectServer(namespace, null, null, null, 0, null, null); + // Release the locator. If successful, pSvc contains connection + // information + loc.Release(); + + // Step 5: -------------------------------------------------- + // Set security levels on the proxy ------------------------- + HRESULT hres = Ole32.INSTANCE.CoSetProxyBlanket(services, Ole32.RPC_C_AUTHN_WINNT, Ole32.RPC_C_AUTHZ_NONE, null, + Ole32.RPC_C_AUTHN_LEVEL_CALL, Ole32.RPC_C_IMP_LEVEL_IMPERSONATE, null, Ole32.EOAC_NONE); + if (COMUtils.FAILED(hres)) { + services.Release(); + throw new COMException("Could not set proxy blanket.", hres); + } + return services; + } + +} diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Ole32.java b/contrib/platform/src/com/sun/jna/platform/win32/Ole32.java index eaf6f4a4f8..5895ccb9b2 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Ole32.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Ole32.java @@ -25,11 +25,14 @@ import com.sun.jna.Native; import com.sun.jna.Pointer; +import com.sun.jna.platform.win32.COM.Unknown; import com.sun.jna.platform.win32.Guid.CLSID; import com.sun.jna.platform.win32.Guid.GUID; +import com.sun.jna.platform.win32.WTypes.LPOLESTR; import com.sun.jna.platform.win32.WinDef.DWORD; import com.sun.jna.platform.win32.WinDef.LPVOID; import com.sun.jna.platform.win32.WinNT.HRESULT; +import com.sun.jna.platform.win32.WinNT.SECURITY_DESCRIPTOR; import com.sun.jna.ptr.PointerByReference; import com.sun.jna.win32.StdCallLibrary; import com.sun.jna.win32.W32APIOptions; @@ -39,6 +42,7 @@ * * @author dblock[at]dblock.org * @author Tobias Wolf, wolf.tobias@gmx.net + * @author widdis[at]gmail.com */ public interface Ole32 extends StdCallLibrary { @@ -134,6 +138,215 @@ public interface Ole32 extends StdCallLibrary { */ HRESULT CoInitializeEx(Pointer reserved, int dwCoInit); + int RPC_C_AUTHN_LEVEL_DEFAULT = 0; + int RPC_C_AUTHN_WINNT = 10; + int RPC_C_IMP_LEVEL_IMPERSONATE = 3; + int RPC_C_AUTHZ_NONE = 0; + int RPC_C_AUTHN_LEVEL_CALL = 3; + + int EOAC_NONE = 0; + + /** + * Registers security and sets the default security values for the process. + * + * @param pSecDesc + * [in, optional] The access permissions that a server will use + * to receive calls. This parameter is used by COM only when a + * server calls CoInitializeSecurity. Its value is a pointer to + * one of three types: an AppID, an IAccessControl object, or a + * SECURITY_DESCRIPTOR, in absolute format. See the Remarks + * section for more information. + * @param cAuthSvc + * [in] The count of entries in the asAuthSvc parameter. This + * parameter is used by COM only when a server calls + * CoInitializeSecurity. If this parameter is 0, no + * authentication services will be registered and the server + * cannot receive secure calls. A value of -1 tells COM to choose + * which authentication services to register, and if this is the + * case, the asAuthSvc parameter must be NULL. However, Schannel + * will never be chosen as an authentication service by the + * server if this parameter is -1. + * @param asAuthSvc + * [in, optional] An array of authentication services that a + * server is willing to use to receive a call. This parameter is + * used by COM only when a server calls CoInitializeSecurity. For + * more information, see SOLE_AUTHENTICATION_SERVICE. + * @param pReserved1 + * [in, optional] This parameter is reserved and must be NULL. + * @param dwAuthnLevel + * [in] The default authentication level for the process. Both + * servers and clients use this parameter when they call + * CoInitializeSecurity. COM will fail calls that arrive with a + * lower authentication level. By default, all proxies will use + * at least this authentication level. This value should contain + * one of the authentication level constants. By default, all + * calls to IUnknown are made at this level. + * @param dwImpLevel + * [in] The default impersonation level for proxies. The value of + * this parameter is used only when the process is a client. It + * should be a value from the impersonation level constants, + * except for RPC_C_IMP_LEVEL_DEFAULT, which is not for use with + * CoInitializeSecurity. Outgoing calls from the client always + * use the impersonation level as specified. (It is not + * negotiated.) Incoming calls to the client can be at any + * impersonation level. By default, all IUnknown calls are made + * with this impersonation level, so even security-aware + * applications should set this level carefully. To determine + * which impersonation levels each authentication service + * supports, see the description of the authentication services + * in COM and Security Packages. For more information about + * impersonation levels, see Impersonation. + * @param pAuthList + * [in, optional] A pointer to SOLE_AUTHENTICATION_LIST, which is + * an array of SOLE_AUTHENTICATION_INFO structures. This list + * indicates the information for each authentication service that + * a client can use to call a server. This parameter is used by + * COM only when a client calls CoInitializeSecurity. + * @param dwCapabilities + * [in] Additional capabilities of the client or server, + * specified by setting one or more + * EOLE_AUTHENTICATION_CAPABILITIES values. Some of these value + * cannot be used simultaneously, and some cannot be set when + * particular authentication services are being used. + * @param pReserved3 + * [in, optional] This parameter is reserved and must be NULL. + * @return This function can return the standard return value E_INVALIDARG, + * as well as the following values. + * + * S_OK Indicates success. + * + * RPC_E_TOO_LATE CoInitializeSecurity has already been called. + * + * RPC_E_NO_GOOD_SECURITY_PACKAGES The asAuthSvc parameter was not + * NULL, and none of the authentication services in the list could + * be registered. Check the results saved in asAuthSvc for + * authentication service–specific error codes. + * + * E_OUT_OF_MEMORY Out of memory. + */ + HRESULT CoInitializeSecurity(SECURITY_DESCRIPTOR pSecDesc, int cAuthSvc, Pointer asAuthSvc, Pointer pReserved1, + int dwAuthnLevel, int dwImpLevel, Pointer pAuthList, int dwCapabilities, Pointer pReserved3); + + /** + * Sets the authentication information that will be used to make calls on + * the specified proxy. This is a helper function for + * IClientSecurity::SetBlanket. + * + * @param pProxy + * [in] The proxy to be set. + * @param dwAuthnSvc + * [in] The authentication service to be used. For a list of + * possible values, see Authentication Service Constants. Use + * RPC_C_AUTHN_NONE if no authentication is required. If + * RPC_C_AUTHN_DEFAULT is specified, DCOM will pick an + * authentication service following its normal security blanket + * negotiation algorithm. + * @param dwAuthzSvc + * [in] The authorization service to be used. For a list of + * possible values, see Authorization Constants. If + * RPC_C_AUTHZ_DEFAULT is specified, DCOM will pick an + * authorization service following its normal security blanket + * negotiation algorithm. RPC_C_AUTHZ_NONE should be used as the + * authorization service if NTLMSSP, Kerberos, or Schannel is + * used as the authentication service. + * @param pServerPrincName + * [in, optional] The server principal name to be used with the + * authentication service. If COLE_DEFAULT_PRINCIPAL is + * specified, DCOM will pick a principal name using its security + * blanket negotiation algorithm. If Kerberos is used as the + * authentication service, this value must not be NULL. It must + * be the correct principal name of the server or the call will + * fail. If Schannel is used as the authentication service, this + * value must be one of the msstd or fullsic forms described in + * Principal Names, or NULL if you do not want mutual + * authentication. Generally, specifying NULL will not reset the + * server principal name on the proxy; rather, the previous + * setting will be retained. You must be careful when using NULL + * as pServerPrincName when selecting a different authentication + * service for the proxy, because there is no guarantee that the + * previously set principal name would be valid for the newly + * selected authentication service. + * @param dwAuthnLevel + * [in] The authentication level to be used. For a list of + * possible values, see Authentication Level Constants. If + * RPC_C_AUTHN_LEVEL_DEFAULT is specified, DCOM will pick an + * authentication level following its normal security blanket + * negotiation algorithm. If this value is none, the + * authentication service must also be none. + * @param dwImpLevel + * [in] The impersonation level to be used. For a list of + * possible values, see Impersonation Level Constants. If + * RPC_C_IMP_LEVEL_DEFAULT is specified, DCOM will pick an + * impersonation level following its normal security blanket + * negotiation algorithm. If NTLMSSP is the authentication + * service, this value must be RPC_C_IMP_LEVEL_IMPERSONATE or + * RPC_C_IMP_LEVEL_IDENTIFY. NTLMSSP also supports delegate-level + * impersonation (RPC_C_IMP_LEVEL_DELEGATE) on the same computer. + * If Schannel is the authentication service, this parameter must + * be RPC_C_IMP_LEVEL_IMPERSONATE. + * @param pAuthInfo + * [in, optional] A pointer to an RPC_AUTH_IDENTITY_HANDLE value + * that establishes the identity of the client. The format of the + * structure referred to by the handle depends on the provider of + * the authentication service. For calls on the same computer, + * RPC logs on the user with the supplied credentials and uses + * the resulting token for the method call. For NTLMSSP or + * Kerberos, the structure is a SEC_WINNT_AUTH_IDENTITY or + * SEC_WINNT_AUTH_IDENTITY_EX structure. The client can discard + * pAuthInfo after calling the API. RPC does not keep a copy of + * the pAuthInfo pointer, and the client cannot retrieve it later + * in the CoQueryProxyBlanket method. If this parameter is NULL, + * DCOM uses the current proxy identity (which is either the + * process token or the impersonation token). If the handle + * refers to a structure, that identity is used. For Schannel, + * this parameter must be either a pointer to a CERT_CONTEXT + * structure that contains the client's X.509 certificate or is + * NULL if the client wishes to make an anonymous connection to + * the server. If a certificate is specified, the caller must not + * free it as long as any proxy to the object exists in the + * current apartment. For Snego, this member is either NULL, + * points to a SEC_WINNT_AUTH_IDENTITY structure, or points to a + * SEC_WINNT_AUTH_IDENTITY_EX structure. If it is NULL, Snego + * will pick a list of authentication services based on those + * available on the client computer. If it points to a + * SEC_WINNT_AUTH_IDENTITY_EX structure, the structure's + * PackageList member must point to a string containing a + * comma-separated list of authentication service names and the + * PackageListLength member must give the number of bytes in the + * PackageList string. If PackageList is NULL, all calls using + * Snego will fail. If COLE_DEFAULT_AUTHINFO is specified for + * this parameter, DCOM will pick the authentication information + * following its normal security blanket negotiation algorithm. + * CoSetProxyBlanket will fail if pAuthInfo is set and one of the + * cloaking flags is set in the dwCapabilities parameter. + * @param dwCapabilities + * [in] The capabilities of this proxy. For a list of possible + * values, see the EOLE_AUTHENTICATION_CAPABILITIES enumeration. + * The only flags that can be set through this function are + * EOAC_MUTUAL_AUTH, EOAC_STATIC_CLOAKING, EOAC_DYNAMIC_CLOAKING, + * EOAC_ANY_AUTHORITY (this flag is deprecated), + * EOAC_MAKE_FULLSIC, and EOAC_DEFAULT. Either + * EOAC_STATIC_CLOAKING or EOAC_DYNAMIC_CLOAKING can be set if + * pAuthInfo is not set and Schannel is not the authentication + * service. (See Cloaking for more information.) If any + * capability flags other than those mentioned here are set, + * CoSetProxyBlanket will fail. + * @return This function can return the following values. + * + * S_OK The function was successful. + * + * E_INVALIDARG One or more arguments is invalid. + */ + HRESULT CoSetProxyBlanket(Unknown pProxy, // + int dwAuthnSvc, // + int dwAuthzSvc, // + LPOLESTR pServerPrincName, // + int dwAuthnLevel, // + int dwImpLevel, // + Pointer pAuthInfo, // RPC_AUTH_IDENTITY_HANDLE + int dwCapabilities// + ); + /** * Closes the COM library on the current thread, unloads all DLLs loaded by * the thread, frees any other resources that the thread maintains, and diff --git a/contrib/platform/test/com/sun/jna/platform/win32/COM/WbemcliTest.java b/contrib/platform/test/com/sun/jna/platform/win32/COM/WbemcliTest.java new file mode 100644 index 0000000000..7422e5b178 --- /dev/null +++ b/contrib/platform/test/com/sun/jna/platform/win32/COM/WbemcliTest.java @@ -0,0 +1,285 @@ +/* Copyright (c) 2018 Daniel Widdis, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.win32.COM; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.concurrent.TimeoutException; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.sun.jna.platform.win32.Ole32; +import com.sun.jna.platform.win32.Variant; +import com.sun.jna.platform.win32.COM.Wbemcli.IEnumWbemClassObject; +import com.sun.jna.platform.win32.COM.Wbemcli.IWbemClassObject; +import com.sun.jna.platform.win32.COM.WbemcliUtil.WmiQuery; +import com.sun.jna.platform.win32.COM.WbemcliUtil.WmiResult; +import com.sun.jna.platform.win32.OleAuto; +import com.sun.jna.ptr.IntByReference; + +/** + * Test class for Wbemcli and WbemcliUti methods and classes used to query WMI. + * Also tests some methods of Ole32. + */ +public class WbemcliTest { + + /** + * Properties to retrieve from Win32_Process + */ + enum ProcessProperty { + PROCESSID, // UINT32 + WORKINGSETSIZE, // UINT64 + CREATIONDATE, // DATETIME + EXECUTIONSTATE, // UINT16, Always NULL + COMMANDLINE; // STRING + } + + /** + * Properties to retrieve from Win32_OperatingSystem + */ + enum OperatingSystemProperty { + FOREGROUNDAPPLICATIONBOOST, // UINT8 + OSTYPE, // UINT16 + PRIMARY; // BOOLEAN + } + + @Before + public void initCom() { + assertEquals(COMUtils.S_OK, Ole32.INSTANCE.CoInitializeEx(null, Ole32.COINIT_MULTITHREADED).intValue()); + assertEquals(COMUtils.S_OK, + Ole32.INSTANCE.CoInitializeSecurity(null, -1, null, null, Ole32.RPC_C_AUTHN_LEVEL_DEFAULT, + Ole32.RPC_C_IMP_LEVEL_IMPERSONATE, null, Ole32.EOAC_NONE, null).intValue()); + } + + @After + public void unInitCom() { + Ole32.INSTANCE.CoUninitialize(); + } + + @Test + public void testWmiExceptions() { + WmiQuery processQuery = new WmiQuery("Win32_Process", ProcessProperty.class); + try { + // This query should take more than 0 ms + processQuery.execute(0); + // Highly unlikely to get this far, but if we do, no failure + System.err.println("Warning: Win32_Process WMI query returned in 0 ms. This is unusual."); + } catch (TimeoutException expected) { + assertEquals("No results after 0 ms.", expected.getMessage()); + } + + // Invalid class + processQuery.setWmiClassName("Win32_ClassDoesNotExist"); + try { + processQuery.execute(); + fail("Win32_ClassDoesNotExist does not exist."); + } catch (COMException expected) { + assertEquals(Wbemcli.WBEM_E_INVALID_CLASS, expected.getHresult().intValue()); + } + + // Valid class but properties don't match the class + processQuery.setWmiClassName("Win32_OperatingSystem"); + try { + processQuery.execute(); + fail("Properties in the process enum aren't in Win32_OperatingSystem"); + } catch (COMException expected) { + assertEquals(Wbemcli.WBEM_E_INVALID_QUERY, expected.getHresult().intValue()); + } + + // Invalid namespace + processQuery.setNameSpace("Invalid"); + try { + processQuery.execute(); + fail("This is an invalid namespace."); + } catch (COMException expected) { + assertEquals(Wbemcli.WBEM_E_INVALID_NAMESPACE, expected.getHresult().intValue()); + } + } + + @Test + public void testNamespace() { + assertTrue(WbemcliUtil.hasNamespace(WbemcliUtil.DEFAULT_NAMESPACE)); + assertFalse(WbemcliUtil.hasNamespace("Name Space")); + } + + @Test + public void testWmiProcesses() { + WmiQuery processQuery = new WmiQuery("Win32_Process", ProcessProperty.class); + + WmiResult processes = processQuery.execute(); + // There has to be at least one process (this one!) + assertTrue(processes.getResultCount() > 0); + int lastProcessIndex = processes.getResultCount() - 1; + + // PID is UINT32 = VT_I4 + assertEquals(Wbemcli.CIM_UINT32, processes.getCIMType(ProcessProperty.PROCESSID)); + assertEquals(Variant.VT_I4, processes.getVtType(ProcessProperty.PROCESSID)); + assertTrue((Integer) processes.getValue(ProcessProperty.PROCESSID, lastProcessIndex) >= 0); + + // WSS is UINT64 = STRING + assertEquals(Wbemcli.CIM_UINT64, processes.getCIMType(ProcessProperty.WORKINGSETSIZE)); + assertEquals(Variant.VT_BSTR, processes.getVtType(ProcessProperty.WORKINGSETSIZE)); + String wssStr = (String) processes.getValue(ProcessProperty.WORKINGSETSIZE, lastProcessIndex); + assertTrue(Long.parseLong(wssStr) > 0); + + // EXECUTIONSTATE is UINT16 but is always null + assertEquals(Wbemcli.CIM_UINT16, processes.getCIMType(ProcessProperty.EXECUTIONSTATE)); + assertEquals(Variant.VT_NULL, processes.getVtType(ProcessProperty.EXECUTIONSTATE)); + Object state = processes.getValue(ProcessProperty.EXECUTIONSTATE, lastProcessIndex); + assertNull(state); + + // CreationDate is DATETIME = STRING + // and be in CIM_DATETIME format yyyymmddhhmmss.mmmmmm+zzz + assertEquals(Wbemcli.CIM_DATETIME, processes.getCIMType(ProcessProperty.CREATIONDATE)); + assertEquals(Variant.VT_BSTR, processes.getVtType(ProcessProperty.CREATIONDATE)); + String cdate = (String) processes.getValue(ProcessProperty.CREATIONDATE, lastProcessIndex); + + assertEquals(25, cdate.length()); + assertEquals('.', cdate.charAt(14)); + assertTrue(Integer.parseInt(cdate.substring(0, 4)) > 1970); + assertTrue(Integer.parseInt(cdate.substring(4, 6)) <= 12); + assertTrue(Integer.parseInt(cdate.substring(6, 8)) <= 31); + } + + + @Test + public void testShowProperties() { + Wbemcli.IWbemServices svc = null; + IEnumWbemClassObject enumRes = null; + Variant.VARIANT.ByReference pVal = new Variant.VARIANT.ByReference(); + IntByReference pType = new IntByReference(); + IntByReference plFlavor = new IntByReference(); + int resultCount = 0; + try { + svc = WbemcliUtil.connectServer(WbemcliUtil.DEFAULT_NAMESPACE); + enumRes = svc.ExecQuery("WQL", "SELECT * FROM Win32_Process WHERE COMMANDLINE IS NOT NULL", Wbemcli.WBEM_FLAG_FORWARD_ONLY, null); + while(true) { + IWbemClassObject[] results = enumRes.Next(Wbemcli.WBEM_INFINITE, 100); + if(results.length == 0) { + break; + } + + for(IWbemClassObject iwco: results) { + resultCount++; + try { + // COMMANDLINE is STRING = VT_BSTR + iwco.Get("COMMANDLINE", 0, pVal, pType, plFlavor); + assertEquals(Wbemcli.CIM_STRING, pType.getValue()); + assertEquals(Variant.VT_BSTR, pVal.getVarType().intValue()); + String commandline = pVal.stringValue(); + assertTrue(commandline != null && (!commandline.isEmpty())); + OleAuto.INSTANCE.VariantClear(pVal); + + // PID is UINT32 = VT_I4 + iwco.Get("PROCESSID", 0, pVal, pType, plFlavor); + assertEquals(Wbemcli.CIM_UINT32, pType.getValue()); + assertEquals(Variant.VT_I4, pVal.getVarType().intValue()); + long processId = pVal.longValue(); + assertTrue(processId >= 0); + OleAuto.INSTANCE.VariantClear(pVal); + + // WSS is UINT64 = STRING + iwco.Get("WORKINGSETSIZE", 0, pVal, pType, plFlavor); + assertEquals(Wbemcli.CIM_UINT64, pType.getValue()); + assertEquals(Variant.VT_BSTR, pVal.getVarType().intValue()); + String workingSetSizeString = pVal.stringValue(); + long workingSetSize = Long.parseLong(workingSetSizeString); + assertTrue(workingSetSize > 0); + OleAuto.INSTANCE.VariantClear(pVal); + + // EXECUTIONSTATE is UINT16 but is always null + iwco.Get("EXECUTIONSTATE", 0, pVal, pType, plFlavor); + assertEquals(Wbemcli.CIM_UINT16, pType.getValue()); + assertEquals(Variant.VT_NULL, pVal.getVarType().intValue()); + OleAuto.INSTANCE.VariantClear(pVal); + + // CreationDate is DATETIME = STRING + // and be in CIM_DATETIME format yyyymmddhhmmss.mmmmmm+zzz + iwco.Get("CREATIONDATE", 0, pVal, pType, plFlavor); + String cdate = pVal.stringValue(); + assertEquals(Wbemcli.CIM_DATETIME, pType.getValue()); + assertEquals(Variant.VT_BSTR, pVal.getVarType().intValue()); + assertEquals(25, cdate.length()); + assertEquals('.', cdate.charAt(14)); + int year = Integer.parseInt(cdate.substring(0, 4)); + int month = Integer.parseInt(cdate.substring(4, 6)); + int day = Integer.parseInt(cdate.substring(6, 8)); + int hour = Integer.parseInt(cdate.substring(8, 10)); + int minute = Integer.parseInt(cdate.substring(10, 12)); + int second = Integer.parseInt(cdate.substring(12, 14)); + assertTrue(year > 1970); + assertTrue(month >= 1 && month <= 12); + assertTrue(day >= 1 && day <= 31); + assertTrue(hour >= 0 && day <= 23); + assertTrue(minute >= 0 && minute <= 59); + assertTrue(second >= 0 && second <= 59); + OleAuto.INSTANCE.VariantClear(pVal); + +// System.out.printf("% 6d\t% 10d\t%04d-%02d-%02dT%02d:%02d:%02d\t%s%n", processId, workingSetSize, year, month, day, hour, minute, second, commandline); + } finally { + iwco.Release(); + } + } + } + } finally { + if (svc != null) svc.Release(); + if (enumRes != null) enumRes.Release(); + } + + // It is expected, that we can read at least one result (our process) + assertTrue(resultCount > 0); + } + + @Test + public void testWmiOperatingSystem() { + WmiQuery operatingSystemQuery = new WmiQuery("Win32_OperatingSystem", + OperatingSystemProperty.class); + + WmiResult os = operatingSystemQuery.execute(); + // There has to be at least one os (this one!) + assertTrue(os.getResultCount() > 0); + + // ForegroundApplicationBoost is UINT8 = VT_UI1 + assertEquals(Wbemcli.CIM_UINT8, os.getCIMType(OperatingSystemProperty.FOREGROUNDAPPLICATIONBOOST)); + assertEquals(Variant.VT_UI1, os.getVtType(OperatingSystemProperty.FOREGROUNDAPPLICATIONBOOST)); + assertTrue((Byte) os.getValue(OperatingSystemProperty.FOREGROUNDAPPLICATIONBOOST, 0) >= 0); + + // OSTYPE is UINT16 = VT_I4 + assertEquals(Wbemcli.CIM_UINT16, os.getCIMType(OperatingSystemProperty.OSTYPE)); + assertEquals(Variant.VT_I4, os.getVtType(OperatingSystemProperty.OSTYPE)); + assertTrue((Integer) os.getValue(OperatingSystemProperty.OSTYPE, 0) >= 0); + + // PRIMARY is BOOLEAN = VT_BOOL + assertEquals(Wbemcli.CIM_BOOLEAN, os.getCIMType(OperatingSystemProperty.PRIMARY)); + assertEquals(Variant.VT_BOOL, os.getVtType(OperatingSystemProperty.PRIMARY)); + assertNotNull(os.getValue(OperatingSystemProperty.PRIMARY, 0)); + } +}