1
2
3
4
5 package oshi.util.platform.windows;
6
7 import java.lang.reflect.InvocationTargetException;
8 import java.util.HashSet;
9 import java.util.Set;
10 import java.util.concurrent.TimeoutException;
11
12 import org.slf4j.Logger;
13 import org.slf4j.LoggerFactory;
14
15 import com.sun.jna.platform.win32.Ole32;
16 import com.sun.jna.platform.win32.WinError;
17 import com.sun.jna.platform.win32.WinNT;
18 import com.sun.jna.platform.win32.COM.COMException;
19 import com.sun.jna.platform.win32.COM.COMUtils;
20 import com.sun.jna.platform.win32.COM.Wbemcli;
21 import com.sun.jna.platform.win32.COM.WbemcliUtil;
22
23 import oshi.annotation.concurrent.ThreadSafe;
24 import oshi.util.GlobalConfig;
25
26
27
28
29 @ThreadSafe
30 public class WmiQueryHandler {
31
32 private static final Logger LOG = LoggerFactory.getLogger(WmiQueryHandler.class);
33
34 private static int globalTimeout = GlobalConfig.get(GlobalConfig.OSHI_UTIL_WMI_TIMEOUT, -1);
35
36 static {
37 if (globalTimeout == 0 || globalTimeout < -1) {
38 throw new GlobalConfig.PropertyException(GlobalConfig.OSHI_UTIL_WMI_TIMEOUT);
39 }
40 }
41
42
43 private int wmiTimeout = globalTimeout;
44
45
46 private final Set<String> failedWmiClassNames = new HashSet<>();
47
48
49 private int comThreading = Ole32.COINIT_MULTITHREADED;
50
51
52 private boolean securityInitialized = false;
53
54 private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
55 private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
56
57
58 private static Class<? extends WmiQueryHandler> customClass = null;
59
60 protected WmiQueryHandler() {
61
62 }
63
64
65
66
67
68
69
70 public static synchronized WmiQueryHandler createInstance() {
71 if (customClass == null) {
72 return new WmiQueryHandler();
73 }
74 try {
75 return customClass.getConstructor(EMPTY_CLASS_ARRAY).newInstance(EMPTY_OBJECT_ARRAY);
76 } catch (NoSuchMethodException | SecurityException e) {
77 LOG.error("Failed to find or access a no-arg constructor for {}", customClass);
78 } catch (InstantiationException | IllegalAccessException | IllegalArgumentException
79 | InvocationTargetException e) {
80 LOG.error("Failed to create a new instance of {}", customClass);
81 }
82 return null;
83 }
84
85
86
87
88
89
90
91 public static synchronized void setInstanceClass(Class<? extends WmiQueryHandler> instanceClass) {
92 customClass = instanceClass;
93 }
94
95
96
97
98
99
100
101
102
103 public <T extends Enum<T>> WbemcliUtil.WmiResult<T> queryWMI(WbemcliUtil.WmiQuery<T> query) {
104 return queryWMI(query, true);
105 }
106
107
108
109
110
111
112
113
114
115
116
117
118 public <T extends Enum<T>> WbemcliUtil.WmiResult<T> queryWMI(WbemcliUtil.WmiQuery<T> query, boolean initCom) {
119 WbemcliUtil.WmiResult<T> result = WbemcliUtil.INSTANCE.new WmiResult<>(query.getPropertyEnum());
120 if (failedWmiClassNames.contains(query.getWmiClassName())) {
121 return result;
122 }
123 boolean comInit = false;
124 try {
125 if (initCom) {
126 comInit = initCOM();
127 }
128 result = query.execute(wmiTimeout);
129 } catch (COMException e) {
130
131 if (!WmiUtil.OHM_NAMESPACE.equals(query.getNameSpace())) {
132 final int hresult = e.getHresult() == null ? -1 : e.getHresult().intValue();
133 switch (hresult) {
134 case Wbemcli.WBEM_E_INVALID_NAMESPACE:
135 LOG.warn("COM exception: Invalid Namespace {}", query.getNameSpace());
136 break;
137 case Wbemcli.WBEM_E_INVALID_CLASS:
138 LOG.warn("COM exception: Invalid Class {}", query.getWmiClassName());
139 break;
140 case Wbemcli.WBEM_E_INVALID_QUERY:
141 LOG.warn("COM exception: Invalid Query: {}", WmiUtil.queryToString(query));
142 break;
143 default:
144 handleComException(query, e);
145 break;
146 }
147 failedWmiClassNames.add(query.getWmiClassName());
148 }
149 } catch (TimeoutException e) {
150 LOG.warn("WMI query timed out after {} ms: {}", wmiTimeout, WmiUtil.queryToString(query));
151 } finally {
152 if (comInit) {
153 unInitCOM();
154 }
155 }
156 return result;
157 }
158
159
160
161
162
163
164
165 protected void handleComException(WbemcliUtil.WmiQuery<?> query, COMException ex) {
166 LOG.warn(
167 "COM exception querying {}, which might not be on your system. Will not attempt to query it again. Error was {}: {}",
168 query.getWmiClassName(), ex.getHresult() == null ? null : ex.getHresult().intValue(), ex.getMessage());
169 }
170
171
172
173
174
175
176 public boolean initCOM() {
177 boolean comInit = false;
178
179
180 comInit = initCOM(getComThreading());
181 if (!comInit) {
182 comInit = initCOM(switchComThreading());
183 }
184
185
186 if (comInit && !isSecurityInitialized()) {
187 WinNT.HRESULT hres = Ole32.INSTANCE.CoInitializeSecurity(null, -1, null, null,
188 Ole32.RPC_C_AUTHN_LEVEL_DEFAULT, Ole32.RPC_C_IMP_LEVEL_IMPERSONATE, null, Ole32.EOAC_NONE, null);
189
190
191 if (COMUtils.FAILED(hres) && hres.intValue() != WinError.RPC_E_TOO_LATE) {
192 Ole32.INSTANCE.CoUninitialize();
193 throw new COMException("Failed to initialize security.", hres);
194 }
195 securityInitialized = true;
196 }
197 return comInit;
198 }
199
200
201
202
203
204
205
206 protected boolean initCOM(int coInitThreading) {
207 WinNT.HRESULT hres = Ole32.INSTANCE.CoInitializeEx(null, coInitThreading);
208 switch (hres.intValue()) {
209
210
211 case COMUtils.S_OK:
212 case COMUtils.S_FALSE:
213 return true;
214
215 case WinError.RPC_E_CHANGED_MODE:
216 return false;
217
218 default:
219 throw new COMException("Failed to initialize COM library.", hres);
220 }
221 }
222
223
224
225
226 public void unInitCOM() {
227 Ole32.INSTANCE.CoUninitialize();
228 }
229
230
231
232
233
234
235
236 public int getComThreading() {
237 return comThreading;
238 }
239
240
241
242
243
244
245
246 public int switchComThreading() {
247 if (comThreading == Ole32.COINIT_APARTMENTTHREADED) {
248 comThreading = Ole32.COINIT_MULTITHREADED;
249 } else {
250 comThreading = Ole32.COINIT_APARTMENTTHREADED;
251 }
252 return comThreading;
253 }
254
255
256
257
258
259
260 public boolean isSecurityInitialized() {
261 return securityInitialized;
262 }
263
264
265
266
267
268
269
270 public int getWmiTimeout() {
271 return wmiTimeout;
272 }
273
274
275
276
277
278
279 public void setWmiTimeout(int wmiTimeout) {
280 this.wmiTimeout = wmiTimeout;
281 }
282 }