All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oshi.util.platform.windows.WmiQueryHandler Maven / Gradle / Ivy

There is a newer version: 6.6.4
Show newest version
/**
 * MIT License
 *
 * Copyright (c) 2010 - 2020 The OSHI Project Contributors: https://github.com/oshi/oshi/graphs/contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package oshi.util.platform.windows;

import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeoutException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.jna.platform.win32.Ole32; // NOSONAR
import com.sun.jna.platform.win32.WinError;
import com.sun.jna.platform.win32.WinNT;
import com.sun.jna.platform.win32.COM.COMException;
import com.sun.jna.platform.win32.COM.COMUtils;
import com.sun.jna.platform.win32.COM.Wbemcli;
import com.sun.jna.platform.win32.COM.WbemcliUtil;

import oshi.annotation.concurrent.ThreadSafe;
import oshi.util.GlobalConfig;

/**
 * Utility to handle WMI Queries
 */
@ThreadSafe
public class WmiQueryHandler {

    private static final Logger LOG = LoggerFactory.getLogger(WmiQueryHandler.class);

    private static int globalTimeout = GlobalConfig.get("oshi.util.wmi.timeout", -1);

    static {
        if (globalTimeout == 0 || globalTimeout < -1) {
            throw new GlobalConfig.PropertyException("oshi.util.wmi.timeout");
        }
    }

    // Timeout for WMI queries
    private int wmiTimeout = globalTimeout;

    // Cache failed wmi classes
    private final Set failedWmiClassNames = new HashSet<>();

    // Preferred threading model
    private int comThreading = Ole32.COINIT_MULTITHREADED;

    // Track initialization of Security
    private boolean securityInitialized = false;

    private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
    private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];

    // Factory to create this or a subclass
    private static Class customClass = null;

    /**
     * Factory method to create an instance of this class. To override this class,
     * use {@link #setInstanceClass(Class)} to define a sublcass which extends
     * {@link oshi.util.platform.windows.WmiQueryHandler}.
     *
     * @return An instance of this class or a class defined by
     *         {@link #setInstanceClass(Class)}
     */
    public static synchronized WmiQueryHandler createInstance() {
        if (customClass == null) {
            return new WmiQueryHandler();
        }
        try {
            return customClass.getConstructor(EMPTY_CLASS_ARRAY).newInstance(EMPTY_OBJECT_ARRAY);
        } catch (NoSuchMethodException | SecurityException e) {
            LOG.error("Failed to find or access a no-arg constructor for {}", customClass);
        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
                | InvocationTargetException e) {
            LOG.error("Failed to create a new instance of {}", customClass);
        }
        return null;
    }

    /**
     * Define a subclass to be instantiated by {@link #createInstance()}. The class
     * must extend {@link oshi.util.platform.windows.WmiQueryHandler}.
     *
     * @param instanceClass
     *            The class to instantiate with {@link #createInstance()}.
     */
    public static synchronized void setInstanceClass(Class instanceClass) {
        customClass = instanceClass;
    }

    /**
     * Query WMI for values, with no timeout.
     *
     * @param 
     *            WMI queries use an Enum to identify the fields to query, and use
     *            the enum values as keys to retrieve the results.
     * @param query
     *            A WmiQuery object encapsulating the namespace, class, and
     *            properties
     * @return a WmiResult object containing the query results, wrapping an EnumMap
     */
    public > WbemcliUtil.WmiResult queryWMI(WbemcliUtil.WmiQuery query) {

        WbemcliUtil.WmiResult result = WbemcliUtil.INSTANCE.new WmiResult<>(query.getPropertyEnum());
        if (failedWmiClassNames.contains(query.getWmiClassName())) {
            return result;
        }
        boolean comInit = false;
        try {
            comInit = initCOM();
            result = query.execute(wmiTimeout);
        } catch (COMException e) {
            // Ignore any exceptions with OpenHardwareMonitor
            if (!WmiUtil.OHM_NAMESPACE.equals(query.getNameSpace())) {
                final int hresult = e.getHresult() == null ? -1 : e.getHresult().intValue();
                switch (hresult) {
                case Wbemcli.WBEM_E_INVALID_NAMESPACE:
                    LOG.warn("COM exception: Invalid Namespace {}", query.getNameSpace());
                    break;
                case Wbemcli.WBEM_E_INVALID_CLASS:
                    LOG.warn("COM exception: Invalid Class {}", query.getWmiClassName());
                    break;
                case Wbemcli.WBEM_E_INVALID_QUERY:
                    LOG.warn("COM exception: Invalid Query: {}", WmiUtil.queryToString(query));
                    break;
                default:
                    handleComException(query, e);
                    break;
                }
                failedWmiClassNames.add(query.getWmiClassName());
            }
        } catch (TimeoutException e) {
            LOG.error("WMI query timed out after {} ms: {}", wmiTimeout, WmiUtil.queryToString(query));
        }
        if (comInit) {
            unInitCOM();
        }
        return result;
    }

    /**
     * 

* handleComException. *

* * @param query * a {@link com.sun.jna.platform.win32.COM.WbemcliUtil.WmiQuery} * object. * @param ex * a {@link com.sun.jna.platform.win32.COM.COMException} object. */ protected void handleComException(WbemcliUtil.WmiQuery query, COMException ex) { LOG.warn( "COM exception querying {}, which might not be on your system. Will not attempt to query it again. Error was {}: {}", query.getWmiClassName(), ex.getHresult().intValue(), ex.getMessage()); } /** * Initializes COM library and sets security to impersonate the local user * * @return True if COM was initialized and needs to be uninitialized, false * otherwise */ public boolean initCOM() { boolean comInit = false; // Step 1: -------------------------------------------------- // Initialize COM. ------------------------------------------ comInit = initCOM(getComThreading()); if (!comInit) { comInit = initCOM(switchComThreading()); } // Step 2: -------------------------------------------------- // Set general COM security levels -------------------------- if (comInit && !isSecurityInitialized()) { WinNT.HRESULT hres = Ole32.INSTANCE.CoInitializeSecurity(null, -1, null, null, Ole32.RPC_C_AUTHN_LEVEL_DEFAULT, Ole32.RPC_C_IMP_LEVEL_IMPERSONATE, null, Ole32.EOAC_NONE, null); // If security already initialized we get RPC_E_TOO_LATE // This can be safely ignored if (COMUtils.FAILED(hres) && hres.intValue() != WinError.RPC_E_TOO_LATE) { Ole32.INSTANCE.CoUninitialize(); throw new COMException("Failed to initialize security.", hres); } securityInitialized = true; } return comInit; } /** *

* initCOM. *

* * @param coInitThreading * a int. * @return a boolean. */ protected boolean initCOM(int coInitThreading) { WinNT.HRESULT hres = Ole32.INSTANCE.CoInitializeEx(null, coInitThreading); switch (hres.intValue()) { // Successful local initialization (S_OK) or was already initialized // (S_FALSE) but still needs uninit case COMUtils.S_OK: case COMUtils.S_FALSE: return true; // COM was already initialized with a different threading model case WinError.RPC_E_CHANGED_MODE: return false; // Any other results is impossible default: throw new COMException("Failed to initialize COM library.", hres); } } /** * UnInitializes COM library. This should be called once for every successful * call to initCOM. */ public void unInitCOM() { Ole32.INSTANCE.CoUninitialize(); } /** * Returns the current threading model for COM initialization, as OSHI is * required to match if an external program has COM initialized already. * * @return The current threading model */ public int getComThreading() { return comThreading; } /** * Switches the current threading model for COM initialization, as OSHI is * required to match if an external program has COM initialized already. * * @return The new threading model after switching */ public int switchComThreading() { if (comThreading == Ole32.COINIT_APARTMENTTHREADED) { comThreading = Ole32.COINIT_MULTITHREADED; } else { comThreading = Ole32.COINIT_APARTMENTTHREADED; } return comThreading; } /** * Security only needs to be initialized once. This boolean identifies whether * that has happened. * * @return Returns the securityInitialized. */ public boolean isSecurityInitialized() { return securityInitialized; } /** * Gets the current WMI timeout. WMI queries will fail if they take longer than * this number of milliseconds. A value of -1 is infinite (no timeout). * * @return Returns the current value of wmiTimeout. */ public int getWmiTimeout() { return wmiTimeout; } /** * Sets the WMI timeout. WMI queries will fail if they take longer than this * number of milliseconds. * * @param wmiTimeout * The wmiTimeout to set, in milliseconds. To disable timeouts, set * timeout as -1 (infinite). */ public void setWmiTimeout(int wmiTimeout) { this.wmiTimeout = wmiTimeout; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy