View Javadoc
1   /*
2    * Copyright 2016-2023 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.util.platform.windows;
6   
7   import java.time.OffsetDateTime;
8   import java.util.Locale;
9   
10  import com.sun.jna.platform.win32.Variant;
11  import com.sun.jna.platform.win32.COM.Wbemcli;
12  import com.sun.jna.platform.win32.COM.WbemcliUtil.WmiQuery;
13  import com.sun.jna.platform.win32.COM.WbemcliUtil.WmiResult;
14  
15  import oshi.annotation.concurrent.ThreadSafe;
16  import oshi.util.Constants;
17  import oshi.util.ParseUtil;
18  
19  /**
20   * Helper class for WMI
21   */
22  @ThreadSafe
23  public final class WmiUtil {
24  
25      /**
26       * The namespace where Open Hardware Monitor publishes to WMI,
27       * <code>OHM_NAMESPACE="ROOT\\OpenHardwareMonitor"</code>. This namespace is not built-in to WMI, so if OHM is not
28       * running would result in unnecessary log messages.
29       */
30      public static final String OHM_NAMESPACE = "ROOT\\OpenHardwareMonitor";
31  
32      private static final String CLASS_CAST_MSG = "%s is not a %s type. CIM Type is %d and VT type is %d";
33  
34      private WmiUtil() {
35      }
36  
37      /**
38       * Translate a WmiQuery to the actual query string
39       *
40       * @param <T>   WMI queries use an Enum to identify the fields to query, and use the enum values as keys to retrieve
41       *              the results.
42       * @param query The WmiQuery object
43       * @return The string that is queried in WMI
44       */
45      public static <T extends Enum<T>> String queryToString(WmiQuery<T> query) {
46          T[] props = query.getPropertyEnum().getEnumConstants();
47          StringBuilder sb = new StringBuilder("SELECT ");
48          sb.append(props[0].name());
49          for (int i = 1; i < props.length; i++) {
50              sb.append(',').append(props[i].name());
51          }
52          sb.append(" FROM ").append(query.getWmiClassName());
53          return sb.toString();
54      }
55  
56      /**
57       * Gets a String value from a WmiResult
58       *
59       * @param <T>      WMI queries use an Enum to identify the fields to query, and use the enum values as keys to
60       *                 retrieve the results.
61       * @param result   The WmiResult from which to fetch the value
62       * @param property The property (column) to fetch
63       * @param index    The index (row) to fetch
64       * @return The stored value if non-null, an empty-string otherwise
65       */
66      public static <T extends Enum<T>> String getString(WmiResult<T> result, T property, int index) {
67          if (result.getCIMType(property) == Wbemcli.CIM_STRING) {
68              return getStr(result, property, index);
69          }
70          throw new ClassCastException(String.format(Locale.ROOT, CLASS_CAST_MSG, property.name(), "String",
71                  result.getCIMType(property), result.getVtType(property)));
72      }
73  
74      /**
75       * Gets a Date value from a WmiResult as a String in ISO 8601 format
76       *
77       * @param <T>      WMI queries use an Enum to identify the fields to query, and use the enum values as keys to
78       *                 retrieve the results.
79       * @param result   The WmiResult from which to fetch the value
80       * @param property The property (column) to fetch
81       * @param index    The index (row) to fetch
82       * @return The stored value if non-null, an empty-string otherwise
83       */
84      public static <T extends Enum<T>> String getDateString(WmiResult<T> result, T property, int index) {
85          OffsetDateTime dateTime = getDateTime(result, property, index);
86          // Null result returns the Epoch
87          if (dateTime.equals(Constants.UNIX_EPOCH)) {
88              return "";
89          }
90          return dateTime.toLocalDate().toString();
91      }
92  
93      /**
94       * Gets a DateTime value from a WmiResult as an OffsetDateTime
95       *
96       * @param <T>      WMI queries use an Enum to identify the fields to query, and use the enum values as keys to
97       *                 retrieve the results.
98       * @param result   The WmiResult from which to fetch the value
99       * @param property The property (column) to fetch
100      * @param index    The index (row) to fetch
101      * @return The stored value if non-null, otherwise the constant {@link oshi.util.Constants#UNIX_EPOCH}
102      */
103     public static <T extends Enum<T>> OffsetDateTime getDateTime(WmiResult<T> result, T property, int index) {
104         if (result.getCIMType(property) == Wbemcli.CIM_DATETIME) {
105             return ParseUtil.parseCimDateTimeToOffset(getStr(result, property, index));
106         }
107         throw new ClassCastException(String.format(Locale.ROOT, CLASS_CAST_MSG, property.name(), "DateTime",
108                 result.getCIMType(property), result.getVtType(property)));
109     }
110 
111     /**
112      * Gets a Reference value from a WmiResult as a String
113      *
114      * @param <T>      WMI queries use an Enum to identify the fields to query, and use the enum values as keys to
115      *                 retrieve the results.
116      * @param result   The WmiResult from which to fetch the value
117      * @param property The property (column) to fetch
118      * @param index    The index (row) to fetch
119      * @return The stored value if non-null, an empty-string otherwise
120      */
121     public static <T extends Enum<T>> String getRefString(WmiResult<T> result, T property, int index) {
122         if (result.getCIMType(property) == Wbemcli.CIM_REFERENCE) {
123             return getStr(result, property, index);
124         }
125         throw new ClassCastException(String.format(Locale.ROOT, CLASS_CAST_MSG, property.name(), "Reference",
126                 result.getCIMType(property), result.getVtType(property)));
127     }
128 
129     private static <T extends Enum<T>> String getStr(WmiResult<T> result, T property, int index) {
130         Object o = result.getValue(property, index);
131         if (o == null) {
132             return "";
133         } else if (result.getVtType(property) == Variant.VT_BSTR) {
134             return (String) o;
135         }
136         throw new ClassCastException(String.format(Locale.ROOT, CLASS_CAST_MSG, property.name(), "String-mapped",
137                 result.getCIMType(property), result.getVtType(property)));
138     }
139 
140     /**
141      * Gets a Uint64 value from a WmiResult (parsing the String). Note that while the CIM type is unsigned, the return
142      * type is signed and the parsing will exclude any return values above Long.MAX_VALUE.
143      *
144      * @param <T>      WMI queries use an Enum to identify the fields to query, and use the enum values as keys to
145      *                 retrieve the results.
146      * @param result   The WmiResult from which to fetch the value
147      * @param property The property (column) to fetch
148      * @param index    The index (row) to fetch
149      * @return The stored value if non-null and parseable as a long, 0 otherwise
150      */
151     public static <T extends Enum<T>> long getUint64(WmiResult<T> result, T property, int index) {
152         Object o = result.getValue(property, index);
153         if (o == null) {
154             return 0L;
155         } else if (result.getCIMType(property) == Wbemcli.CIM_UINT64 && result.getVtType(property) == Variant.VT_BSTR) {
156             return ParseUtil.parseLongOrDefault((String) o, 0L);
157         }
158         throw new ClassCastException(String.format(Locale.ROOT, CLASS_CAST_MSG, property.name(), "UINT64",
159                 result.getCIMType(property), result.getVtType(property)));
160     }
161 
162     /**
163      * Gets an UINT32 value from a WmiResult. Note that while a UINT32 CIM type is unsigned, the return type is signed
164      * and requires further processing by the user if unsigned values are desired.
165      *
166      * @param <T>      WMI queries use an Enum to identify the fields to query, and use the enum values as keys to
167      *                 retrieve the results.
168      * @param result   The WmiResult from which to fetch the value
169      * @param property The property (column) to fetch
170      * @param index    The index (row) to fetch
171      * @return The stored value if non-null, 0 otherwise
172      */
173     public static <T extends Enum<T>> int getUint32(WmiResult<T> result, T property, int index) {
174         if (result.getCIMType(property) == Wbemcli.CIM_UINT32) {
175             return getInt(result, property, index);
176         }
177         throw new ClassCastException(String.format(Locale.ROOT, CLASS_CAST_MSG, property.name(), "UINT32",
178                 result.getCIMType(property), result.getVtType(property)));
179     }
180 
181     /**
182      * Gets an UINT32 value from a WmiResult as a long, preserving the unsignedness.
183      *
184      * @param <T>      WMI queries use an Enum to identify the fields to query, and use the enum values as keys to
185      *                 retrieve the results.
186      * @param result   The WmiResult from which to fetch the value
187      * @param property The property (column) to fetch
188      * @param index    The index (row) to fetch
189      * @return The stored value if non-null, 0 otherwise
190      */
191     public static <T extends Enum<T>> long getUint32asLong(WmiResult<T> result, T property, int index) {
192         if (result.getCIMType(property) == Wbemcli.CIM_UINT32) {
193             return getInt(result, property, index) & 0xFFFFFFFFL;
194         }
195         throw new ClassCastException(String.format(Locale.ROOT, CLASS_CAST_MSG, property.name(), "UINT32",
196                 result.getCIMType(property), result.getVtType(property)));
197     }
198 
199     /**
200      * Gets a Sint32 value from a WmiResult. Note that while the CIM type is unsigned, the return type is signed and
201      * requires further processing by the user if unsigned values are desired.
202      *
203      * @param <T>      WMI queries use an Enum to identify the fields to query, and use the enum values as keys to
204      *                 retrieve the results.
205      * @param result   The WmiResult from which to fetch the value
206      * @param property The property (column) to fetch
207      * @param index    The index (row) to fetch
208      * @return The stored value if non-null, 0 otherwise
209      */
210     public static <T extends Enum<T>> int getSint32(WmiResult<T> result, T property, int index) {
211         if (result.getCIMType(property) == Wbemcli.CIM_SINT32) {
212             return getInt(result, property, index);
213         }
214         throw new ClassCastException(String.format(Locale.ROOT, CLASS_CAST_MSG, property.name(), "SINT32",
215                 result.getCIMType(property), result.getVtType(property)));
216     }
217 
218     /**
219      * Gets a Uint16 value from a WmiResult. Note that while the CIM type is unsigned, the return type is signed and
220      * requires further processing by the user if unsigned values are desired.
221      *
222      * @param <T>      WMI queries use an Enum to identify the fields to query, and use the enum values as keys to
223      *                 retrieve the results.
224      * @param result   The WmiResult from which to fetch the value
225      * @param property The property (column) to fetch
226      * @param index    The index (row) to fetch
227      * @return The stored value if non-null, 0 otherwise
228      */
229     public static <T extends Enum<T>> int getUint16(WmiResult<T> result, T property, int index) {
230         if (result.getCIMType(property) == Wbemcli.CIM_UINT16) {
231             return getInt(result, property, index);
232         }
233         throw new ClassCastException(String.format(Locale.ROOT, CLASS_CAST_MSG, property.name(), "UINT16",
234                 result.getCIMType(property), result.getVtType(property)));
235     }
236 
237     private static <T extends Enum<T>> int getInt(WmiResult<T> result, T property, int index) {
238         Object o = result.getValue(property, index);
239         if (o == null) {
240             return 0;
241         } else if (result.getVtType(property) == Variant.VT_I4) {
242             return (int) o;
243         }
244         throw new ClassCastException(String.format(Locale.ROOT, CLASS_CAST_MSG, property.name(), "32-bit integer",
245                 result.getCIMType(property), result.getVtType(property)));
246     }
247 
248     /**
249      * Gets a Float value from a WmiResult
250      *
251      * @param <T>      WMI queries use an Enum to identify the fields to query, and use the enum values as keys to
252      *                 retrieve the results.
253      * @param result   The WmiResult from which to fetch the value
254      * @param property The property (column) to fetch
255      * @param index    The index (row) to fetch
256      * @return The stored value if non-null, 0 otherwise
257      */
258     public static <T extends Enum<T>> float getFloat(WmiResult<T> result, T property, int index) {
259         Object o = result.getValue(property, index);
260         if (o == null) {
261             return 0f;
262         } else if (result.getCIMType(property) == Wbemcli.CIM_REAL32 && result.getVtType(property) == Variant.VT_R4) {
263             return (float) o;
264         }
265         throw new ClassCastException(String.format(Locale.ROOT, CLASS_CAST_MSG, property.name(), "Float",
266                 result.getCIMType(property), result.getVtType(property)));
267     }
268 }