com.sun.jna.platform.win32.COM.WbemcliUtil Maven / Gradle / Ivy
/* 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 static com.sun.jna.platform.win32.Variant.VT_ARRAY;
import static com.sun.jna.platform.win32.Variant.VT_DISPATCH;
import static com.sun.jna.platform.win32.Variant.VT_UNKNOWN;
import static com.sun.jna.platform.win32.Variant.VT_VECTOR;
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. Values, that are not supported by this helper
         *         ({@code Dispatch}, {@code Unknown}, {@code SAFEARRAY}) are
         *         not returned and reported as {@code null}.
         *
         * @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_EMPTY:
                        case Variant.VT_NULL:
                            values.add(vtType, cimType, property, null);
                            break;
                        // Unimplemented type. User must cast
                        default:
                            if(((vtType & VT_ARRAY) == VT_ARRAY) ||
                                ((vtType & VT_UNKNOWN) == VT_UNKNOWN)||
                                ((vtType & VT_DISPATCH) == VT_DISPATCH)||
                                ((vtType & VT_VECTOR) == VT_VECTOR)) {
                                values.add(vtType, cimType, property, null);
                            } else {
                                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                            © 2015 - 2025 Weber Informatics LLC | Privacy Policy