1
2
3
4
5 package oshi.software.os.windows;
6
7 import static oshi.software.os.OSService.State.OTHER;
8 import static oshi.software.os.OSService.State.RUNNING;
9 import static oshi.software.os.OSService.State.STOPPED;
10 import static oshi.software.os.OperatingSystem.ProcessFiltering.VALID_PROCESS;
11 import static oshi.util.Memoizer.defaultExpiration;
12 import static oshi.util.Memoizer.installedAppsExpiration;
13 import static oshi.util.Memoizer.memoize;
14
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24 import java.util.concurrent.TimeUnit;
25 import java.util.function.Supplier;
26 import java.util.stream.Collectors;
27
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import com.sun.jna.Native;
32 import com.sun.jna.platform.win32.Advapi32;
33 import com.sun.jna.platform.win32.Advapi32Util;
34 import com.sun.jna.platform.win32.Advapi32Util.EventLogIterator;
35 import com.sun.jna.platform.win32.Advapi32Util.EventLogRecord;
36 import com.sun.jna.platform.win32.Kernel32;
37 import com.sun.jna.platform.win32.Psapi;
38 import com.sun.jna.platform.win32.Tlhelp32;
39 import com.sun.jna.platform.win32.VersionHelpers;
40 import com.sun.jna.platform.win32.W32ServiceManager;
41 import com.sun.jna.platform.win32.Win32Exception;
42 import com.sun.jna.platform.win32.WinDef.DWORD;
43 import com.sun.jna.platform.win32.WinError;
44 import com.sun.jna.platform.win32.WinNT;
45 import com.sun.jna.platform.win32.WinNT.HANDLE;
46 import com.sun.jna.platform.win32.WinNT.LUID;
47 import com.sun.jna.platform.win32.Winsvc;
48 import com.sun.jna.platform.win32.COM.WbemcliUtil.WmiResult;
49
50 import oshi.annotation.concurrent.ThreadSafe;
51 import oshi.driver.windows.EnumWindows;
52 import oshi.driver.windows.registry.HkeyUserData;
53 import oshi.driver.windows.registry.NetSessionData;
54 import oshi.driver.windows.registry.ProcessPerformanceData;
55 import oshi.driver.windows.registry.ProcessWtsData;
56 import oshi.driver.windows.registry.ProcessWtsData.WtsInfo;
57 import oshi.driver.windows.registry.SessionWtsData;
58 import oshi.driver.windows.registry.ThreadPerformanceData;
59 import oshi.driver.windows.wmi.Win32OperatingSystem;
60 import oshi.driver.windows.wmi.Win32OperatingSystem.OSVersionProperty;
61 import oshi.driver.windows.wmi.Win32Processor;
62 import oshi.driver.windows.wmi.Win32Processor.BitnessProperty;
63 import oshi.jna.ByRef.CloseableHANDLEByReference;
64 import oshi.jna.ByRef.CloseableIntByReference;
65 import oshi.jna.ByRef.CloseablePROCESSENTRY32ByReference;
66 import oshi.jna.Struct.CloseablePerformanceInformation;
67 import oshi.jna.Struct.CloseableSystemInfo;
68 import oshi.software.common.AbstractOperatingSystem;
69 import oshi.software.os.ApplicationInfo;
70 import oshi.software.os.FileSystem;
71 import oshi.software.os.InternetProtocolStats;
72 import oshi.software.os.NetworkParams;
73 import oshi.software.os.OSDesktopWindow;
74 import oshi.software.os.OSProcess;
75 import oshi.software.os.OSService;
76 import oshi.software.os.OSService.State;
77 import oshi.software.os.OSSession;
78 import oshi.software.os.OSThread;
79 import oshi.util.Constants;
80 import oshi.util.GlobalConfig;
81 import oshi.util.Memoizer;
82 import oshi.util.platform.windows.WmiUtil;
83 import oshi.util.tuples.Pair;
84
85
86
87
88
89 @ThreadSafe
90 public class WindowsOperatingSystem extends AbstractOperatingSystem {
91
92 private static final Logger LOG = LoggerFactory.getLogger(WindowsOperatingSystem.class);
93
94 private static final boolean USE_PROCSTATE_SUSPENDED = GlobalConfig
95 .get(GlobalConfig.OSHI_OS_WINDOWS_PROCSTATE_SUSPENDED, false);
96
97 private static final boolean IS_VISTA_OR_GREATER = VersionHelpers.IsWindowsVistaOrGreater();
98
99
100
101
102 private static Supplier<String> systemLog = memoize(WindowsOperatingSystem::querySystemLog,
103 TimeUnit.HOURS.toNanos(1));
104
105 private static final long BOOTTIME = querySystemBootTime();
106
107 static {
108 enableDebugPrivilege();
109 }
110
111
112
113
114 private static final boolean X86 = isCurrentX86();
115 private static final boolean WOW = isCurrentWow();
116
117 private final Supplier<List<ApplicationInfo>> installedAppsSupplier = Memoizer
118 .memoize(WindowsInstalledApps::queryInstalledApps, installedAppsExpiration());
119
120
121
122
123 private Supplier<Map<Integer, ProcessPerformanceData.PerfCounterBlock>> processMapFromRegistry = memoize(
124 WindowsOperatingSystem::queryProcessMapFromRegistry, defaultExpiration());
125 private Supplier<Map<Integer, ProcessPerformanceData.PerfCounterBlock>> processMapFromPerfCounters = memoize(
126 WindowsOperatingSystem::queryProcessMapFromPerfCounters, defaultExpiration());
127
128
129
130
131 private Supplier<Map<Integer, ThreadPerformanceData.PerfCounterBlock>> threadMapFromRegistry = memoize(
132 WindowsOperatingSystem::queryThreadMapFromRegistry, defaultExpiration());
133 private Supplier<Map<Integer, ThreadPerformanceData.PerfCounterBlock>> threadMapFromPerfCounters = memoize(
134 WindowsOperatingSystem::queryThreadMapFromPerfCounters, defaultExpiration());
135
136 @Override
137 public String queryManufacturer() {
138 return "Microsoft";
139 }
140
141 @Override
142 public Pair<String, OSVersionInfo> queryFamilyVersionInfo() {
143 String version = System.getProperty("os.name");
144 if (version.startsWith("Windows ")) {
145 version = version.substring(8);
146 }
147
148 String sp = null;
149 int suiteMask = 0;
150 String buildNumber = "";
151 WmiResult<OSVersionProperty> versionInfo = Win32OperatingSystem.queryOsVersion();
152 if (versionInfo.getResultCount() > 0) {
153 sp = WmiUtil.getString(versionInfo, OSVersionProperty.CSDVERSION, 0);
154 if (!sp.isEmpty() && !Constants.UNKNOWN.equals(sp)) {
155 version = version + " " + sp.replace("Service Pack ", "SP");
156 }
157 suiteMask = WmiUtil.getUint32(versionInfo, OSVersionProperty.SUITEMASK, 0);
158 buildNumber = WmiUtil.getString(versionInfo, OSVersionProperty.BUILDNUMBER, 0);
159 }
160 String codeName = parseCodeName(suiteMask);
161
162 if ("10".equals(version) && buildNumber.compareTo("22000") >= 0) {
163 version = "11";
164 }
165 if ("Server 2016".equals(version) && buildNumber.compareTo("17762") > 0) {
166 version = "Server 2019";
167 }
168 if ("Server 2019".equals(version) && buildNumber.compareTo("20347") > 0) {
169 version = "Server 2022";
170 }
171 if ("Server 2022".equals(version) && buildNumber.compareTo("26039") > 0) {
172 version = "Server 2025";
173 }
174 return new Pair<>("Windows", new OSVersionInfo(version, codeName, buildNumber));
175 }
176
177
178
179
180
181
182
183
184 private static String parseCodeName(int suiteMask) {
185 List<String> suites = new ArrayList<>();
186 if ((suiteMask & 0x00000002) != 0) {
187 suites.add("Enterprise");
188 }
189 if ((suiteMask & 0x00000004) != 0) {
190 suites.add("BackOffice");
191 }
192 if ((suiteMask & 0x00000008) != 0) {
193 suites.add("Communications Server");
194 }
195 if ((suiteMask & 0x00000080) != 0) {
196 suites.add("Datacenter");
197 }
198 if ((suiteMask & 0x00000200) != 0) {
199 suites.add("Home");
200 }
201 if ((suiteMask & 0x00000400) != 0) {
202 suites.add("Web Server");
203 }
204 if ((suiteMask & 0x00002000) != 0) {
205 suites.add("Storage Server");
206 }
207 if ((suiteMask & 0x00004000) != 0) {
208 suites.add("Compute Cluster");
209 }
210 if ((suiteMask & 0x00008000) != 0) {
211 suites.add("Home Server");
212 }
213 return String.join(",", suites);
214 }
215
216 @Override
217 protected int queryBitness(int jvmBitness) {
218 if (jvmBitness < 64 && System.getenv("ProgramFiles(x86)") != null && IS_VISTA_OR_GREATER) {
219 WmiResult<BitnessProperty> bitnessMap = Win32Processor.queryBitness();
220 if (bitnessMap.getResultCount() > 0) {
221 return WmiUtil.getUint16(bitnessMap, BitnessProperty.ADDRESSWIDTH, 0);
222 }
223 }
224 return jvmBitness;
225 }
226
227 @Override
228 public boolean isElevated() {
229 return Advapi32Util.isCurrentProcessElevated();
230 }
231
232 @Override
233 public FileSystem getFileSystem() {
234 return new WindowsFileSystem();
235 }
236
237 @Override
238 public InternetProtocolStats getInternetProtocolStats() {
239 return new WindowsInternetProtocolStats();
240 }
241
242 @Override
243 public List<OSSession> getSessions() {
244 List<OSSession> whoList = HkeyUserData.queryUserSessions();
245 whoList.addAll(SessionWtsData.queryUserSessions());
246 whoList.addAll(NetSessionData.queryUserSessions());
247 return whoList;
248 }
249
250 @Override
251 public List<OSProcess> getProcesses(Collection<Integer> pids) {
252 return processMapToList(pids);
253 }
254
255 @Override
256 public List<OSProcess> queryAllProcesses() {
257 return processMapToList(null);
258 }
259
260 @Override
261 public List<OSProcess> queryChildProcesses(int parentPid) {
262 Set<Integer> descendantPids = getChildrenOrDescendants(getParentPidsFromSnapshot(), parentPid, false);
263 return processMapToList(descendantPids);
264 }
265
266 @Override
267 public List<OSProcess> queryDescendantProcesses(int parentPid) {
268 Set<Integer> descendantPids = getChildrenOrDescendants(getParentPidsFromSnapshot(), parentPid, true);
269 return processMapToList(descendantPids);
270 }
271
272 private static Map<Integer, Integer> getParentPidsFromSnapshot() {
273 Map<Integer, Integer> parentPidMap = new HashMap<>();
274
275 try (CloseablePROCESSENTRY32ByReference processEntry = new CloseablePROCESSENTRY32ByReference()) {
276 WinNT.HANDLE snapshot = Kernel32.INSTANCE.CreateToolhelp32Snapshot(Tlhelp32.TH32CS_SNAPPROCESS,
277 new DWORD(0));
278 try {
279 while (Kernel32.INSTANCE.Process32Next(snapshot, processEntry)) {
280 parentPidMap.put(processEntry.th32ProcessID.intValue(),
281 processEntry.th32ParentProcessID.intValue());
282 }
283 } finally {
284 Kernel32.INSTANCE.CloseHandle(snapshot);
285 }
286 }
287 return parentPidMap;
288 }
289
290 @Override
291 public OSProcess getProcess(int pid) {
292 List<OSProcess> procList = processMapToList(Arrays.asList(pid));
293 return procList.isEmpty() ? null : procList.get(0);
294 }
295
296 private List<OSProcess> processMapToList(Collection<Integer> pids) {
297
298 Map<Integer, ProcessPerformanceData.PerfCounterBlock> processMap = processMapFromRegistry.get();
299
300 if (processMap == null || processMap.isEmpty()) {
301 processMap = (pids == null) ? processMapFromPerfCounters.get()
302 : ProcessPerformanceData.buildProcessMapFromPerfCounters(pids);
303 }
304 Map<Integer, ThreadPerformanceData.PerfCounterBlock> threadMap = null;
305 if (USE_PROCSTATE_SUSPENDED) {
306
307 threadMap = threadMapFromRegistry.get();
308
309 if (threadMap == null || threadMap.isEmpty()) {
310 threadMap = (pids == null) ? threadMapFromPerfCounters.get()
311 : ThreadPerformanceData.buildThreadMapFromPerfCounters(pids);
312 }
313 }
314
315 Map<Integer, WtsInfo> processWtsMap = ProcessWtsData.queryProcessWtsMap(pids);
316
317 Set<Integer> mapKeys = new HashSet<>(processWtsMap.keySet());
318 mapKeys.retainAll(processMap.keySet());
319
320 final Map<Integer, ProcessPerformanceData.PerfCounterBlock> finalProcessMap = processMap;
321 final Map<Integer, ThreadPerformanceData.PerfCounterBlock> finalThreadMap = threadMap;
322 return mapKeys.stream().parallel()
323 .map(pid -> new WindowsOSProcess(pid, this, finalProcessMap, processWtsMap, finalThreadMap))
324 .filter(VALID_PROCESS).collect(Collectors.toList());
325 }
326
327 private static Map<Integer, ProcessPerformanceData.PerfCounterBlock> queryProcessMapFromRegistry() {
328 return ProcessPerformanceData.buildProcessMapFromRegistry(null);
329 }
330
331 private static Map<Integer, ProcessPerformanceData.PerfCounterBlock> queryProcessMapFromPerfCounters() {
332 return ProcessPerformanceData.buildProcessMapFromPerfCounters(null);
333 }
334
335 private static Map<Integer, ThreadPerformanceData.PerfCounterBlock> queryThreadMapFromRegistry() {
336 return ThreadPerformanceData.buildThreadMapFromRegistry(null);
337 }
338
339 private static Map<Integer, ThreadPerformanceData.PerfCounterBlock> queryThreadMapFromPerfCounters() {
340 return ThreadPerformanceData.buildThreadMapFromPerfCounters(null);
341 }
342
343 @Override
344 public int getProcessId() {
345 return Kernel32.INSTANCE.GetCurrentProcessId();
346 }
347
348 @Override
349 public int getProcessCount() {
350 try (CloseablePerformanceInformation perfInfo = new CloseablePerformanceInformation()) {
351 if (!Psapi.INSTANCE.GetPerformanceInfo(perfInfo, perfInfo.size())) {
352 LOG.error("Failed to get Performance Info. Error code: {}", Kernel32.INSTANCE.GetLastError());
353 return 0;
354 }
355 return perfInfo.ProcessCount.intValue();
356 }
357 }
358
359 @Override
360 public int getThreadId() {
361 return Kernel32.INSTANCE.GetCurrentThreadId();
362 }
363
364 @Override
365 public OSThread getCurrentThread() {
366 OSProcess proc = getCurrentProcess();
367 final int tid = getThreadId();
368 return proc.getThreadDetails().stream().filter(t -> t.getThreadId() == tid).findFirst()
369 .orElse(new WindowsOSThread(proc.getProcessID(), tid, null, null));
370 }
371
372 @Override
373 public int getThreadCount() {
374 try (CloseablePerformanceInformation perfInfo = new CloseablePerformanceInformation()) {
375 if (!Psapi.INSTANCE.GetPerformanceInfo(perfInfo, perfInfo.size())) {
376 LOG.error("Failed to get Performance Info. Error code: {}", Kernel32.INSTANCE.GetLastError());
377 return 0;
378 }
379 return perfInfo.ThreadCount.intValue();
380 }
381 }
382
383 @Override
384 public long getSystemUptime() {
385 return querySystemUptime();
386 }
387
388 private static long querySystemUptime() {
389
390
391 if (IS_VISTA_OR_GREATER) {
392 return Kernel32.INSTANCE.GetTickCount64() / 1000L;
393 } else {
394
395 return Kernel32.INSTANCE.GetTickCount() / 1000L;
396 }
397 }
398
399 @Override
400 public long getSystemBootTime() {
401 return BOOTTIME;
402 }
403
404 private static long querySystemBootTime() {
405 String eventLog = systemLog.get();
406 if (eventLog != null) {
407 try {
408 EventLogIterator iter = new EventLogIterator(null, eventLog, WinNT.EVENTLOG_BACKWARDS_READ);
409
410
411
412 long event6005Time = 0L;
413 while (iter.hasNext()) {
414 EventLogRecord logRecord = iter.next();
415 if (logRecord.getStatusCode() == 12) {
416
417
418 return logRecord.getRecord().TimeGenerated.longValue();
419 } else if (logRecord.getStatusCode() == 6005) {
420
421
422 if (event6005Time > 0) {
423 return event6005Time;
424 }
425
426 event6005Time = logRecord.getRecord().TimeGenerated.longValue();
427 }
428 }
429
430 if (event6005Time > 0) {
431 return event6005Time;
432 }
433 } catch (Win32Exception e) {
434 LOG.warn("Can't open event log \"{}\".", eventLog);
435 }
436 }
437
438
439 return System.currentTimeMillis() / 1000L - querySystemUptime();
440 }
441
442 @Override
443 public NetworkParams getNetworkParams() {
444 return new WindowsNetworkParams();
445 }
446
447
448
449
450
451
452
453 private static boolean enableDebugPrivilege() {
454 try (CloseableHANDLEByReference hToken = new CloseableHANDLEByReference()) {
455 boolean success = Advapi32.INSTANCE.OpenProcessToken(Kernel32.INSTANCE.GetCurrentProcess(),
456 WinNT.TOKEN_QUERY | WinNT.TOKEN_ADJUST_PRIVILEGES, hToken);
457 if (!success) {
458 LOG.error("OpenProcessToken failed. Error: {}", Native.getLastError());
459 return false;
460 }
461 try {
462 LUID luid = new LUID();
463 success = Advapi32.INSTANCE.LookupPrivilegeValue(null, WinNT.SE_DEBUG_NAME, luid);
464 if (!success) {
465 LOG.error("LookupPrivilegeValue failed. Error: {}", Native.getLastError());
466 return false;
467 }
468 WinNT.TOKEN_PRIVILEGES tkp = new WinNT.TOKEN_PRIVILEGES(1);
469 tkp.Privileges[0] = new WinNT.LUID_AND_ATTRIBUTES(luid, new DWORD(WinNT.SE_PRIVILEGE_ENABLED));
470 success = Advapi32.INSTANCE.AdjustTokenPrivileges(hToken.getValue(), false, tkp, 0, null, null);
471 int err = Native.getLastError();
472 if (!success) {
473 LOG.error("AdjustTokenPrivileges failed. Error: {}", err);
474 return false;
475 } else if (err == WinError.ERROR_NOT_ALL_ASSIGNED) {
476 LOG.debug("Debug privileges not enabled.");
477 return false;
478 }
479 } finally {
480 Kernel32.INSTANCE.CloseHandle(hToken.getValue());
481 }
482 }
483 return true;
484 }
485
486 @Override
487 public List<OSService> getServices() {
488 try (W32ServiceManager sm = new W32ServiceManager()) {
489 sm.open(Winsvc.SC_MANAGER_ENUMERATE_SERVICE);
490 Winsvc.ENUM_SERVICE_STATUS_PROCESS[] services = sm.enumServicesStatusExProcess(WinNT.SERVICE_WIN32,
491 Winsvc.SERVICE_STATE_ALL, null);
492 List<OSService> svcArray = new ArrayList<>();
493 for (Winsvc.ENUM_SERVICE_STATUS_PROCESS service : services) {
494 State state;
495 switch (service.ServiceStatusProcess.dwCurrentState) {
496 case 1:
497 state = STOPPED;
498 break;
499 case 4:
500 state = RUNNING;
501 break;
502 default:
503 state = OTHER;
504 break;
505 }
506 svcArray.add(new OSService(service.lpDisplayName, service.ServiceStatusProcess.dwProcessId, state));
507 }
508 return svcArray;
509 } catch (com.sun.jna.platform.win32.Win32Exception ex) {
510 LOG.error("Win32Exception: {}", ex.getMessage());
511 return Collections.emptyList();
512 }
513 }
514
515 private static String querySystemLog() {
516 String systemLog = GlobalConfig.get(GlobalConfig.OSHI_OS_WINDOWS_EVENTLOG, "System");
517 if (systemLog.isEmpty()) {
518
519 return null;
520 }
521
522 HANDLE h = Advapi32.INSTANCE.OpenEventLog(null, systemLog);
523 if (h == null) {
524 LOG.warn("Unable to open configured system Event log \"{}\". Calculating boot time from uptime.",
525 systemLog);
526 return null;
527 }
528 return systemLog;
529 }
530
531 @Override
532 public List<OSDesktopWindow> getDesktopWindows(boolean visibleOnly) {
533 return EnumWindows.queryDesktopWindows(visibleOnly);
534 }
535
536 @Override
537 public List<ApplicationInfo> getInstalledApplications() {
538 return installedAppsSupplier.get();
539 }
540
541
542
543
544
545
546
547
548
549
550 static boolean isX86() {
551 return X86;
552 }
553
554 private static boolean isCurrentX86() {
555 try (CloseableSystemInfo sysinfo = new CloseableSystemInfo()) {
556 Kernel32.INSTANCE.GetNativeSystemInfo(sysinfo);
557 return (0 == sysinfo.processorArchitecture.pi.wProcessorArchitecture.intValue());
558 }
559 }
560
561
562
563
564
565
566 static boolean isWow() {
567 return WOW;
568 }
569
570
571
572
573
574
575
576 static boolean isWow(HANDLE h) {
577 if (X86) {
578 return true;
579 }
580 try (CloseableIntByReference isWow = new CloseableIntByReference()) {
581 Kernel32.INSTANCE.IsWow64Process(h, isWow);
582 return isWow.getValue() != 0;
583 }
584 }
585
586 private static boolean isCurrentWow() {
587 if (X86) {
588 return true;
589 }
590 HANDLE h = Kernel32.INSTANCE.GetCurrentProcess();
591 return (h == null) ? false : isWow(h);
592 }
593 }