View Javadoc
1   /*
2    * Copyright 2020-2022 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.driver.windows.registry;
6   
7   import java.util.Collection;
8   import java.util.HashMap;
9   import java.util.Map;
10  
11  import org.slf4j.Logger;
12  import org.slf4j.LoggerFactory;
13  
14  import com.sun.jna.Pointer;
15  import com.sun.jna.platform.win32.Kernel32;
16  import com.sun.jna.platform.win32.VersionHelpers;
17  import com.sun.jna.platform.win32.Wtsapi32;
18  import com.sun.jna.platform.win32.Wtsapi32.WTS_PROCESS_INFO_EX;
19  import com.sun.jna.platform.win32.COM.WbemcliUtil.WmiResult;
20  
21  import oshi.annotation.concurrent.Immutable;
22  import oshi.annotation.concurrent.ThreadSafe;
23  import oshi.driver.windows.wmi.Win32Process;
24  import oshi.driver.windows.wmi.Win32Process.ProcessXPProperty;
25  import oshi.jna.ByRef.CloseableIntByReference;
26  import oshi.jna.ByRef.CloseablePointerByReference;
27  import oshi.util.platform.windows.WmiUtil;
28  
29  /**
30   * Utility to read process data from HKEY_PERFORMANCE_DATA information with backup from Performance Counters or WMI
31   */
32  @ThreadSafe
33  public final class ProcessWtsData {
34  
35      private static final Logger LOG = LoggerFactory.getLogger(ProcessWtsData.class);
36  
37      private static final boolean IS_WINDOWS7_OR_GREATER = VersionHelpers.IsWindows7OrGreater();
38  
39      private ProcessWtsData() {
40      }
41  
42      /**
43       * Query the registry for process performance counters
44       *
45       * @param pids An optional collection of process IDs to filter the list to. May be null for no filtering.
46       * @return A map with Process ID as the key and a {@link WtsInfo} object populated with data.
47       */
48      public static Map<Integer, WtsInfo> queryProcessWtsMap(Collection<Integer> pids) {
49          if (IS_WINDOWS7_OR_GREATER) {
50              // Get processes from WTS
51              return queryProcessWtsMapFromWTS(pids);
52          }
53          // Pre-Win7 we can't use WTSEnumerateProcessesEx so we'll grab the
54          // same info from WMI and fake the array
55          return queryProcessWtsMapFromPerfMon(pids);
56      }
57  
58      private static Map<Integer, WtsInfo> queryProcessWtsMapFromWTS(Collection<Integer> pids) {
59          Map<Integer, WtsInfo> wtsMap = new HashMap<>();
60          try (CloseableIntByReference pCount = new CloseableIntByReference(0);
61                  CloseablePointerByReference ppProcessInfo = new CloseablePointerByReference();
62                  CloseableIntByReference infoLevel1 = new CloseableIntByReference(Wtsapi32.WTS_PROCESS_INFO_LEVEL_1)) {
63              if (!Wtsapi32.INSTANCE.WTSEnumerateProcessesEx(Wtsapi32.WTS_CURRENT_SERVER_HANDLE, infoLevel1,
64                      Wtsapi32.WTS_ANY_SESSION, ppProcessInfo, pCount)) {
65                  LOG.error("Failed to enumerate Processes. Error code: {}", Kernel32.INSTANCE.GetLastError());
66                  return wtsMap;
67              }
68              // extract the pointed-to pointer and create array
69              Pointer pProcessInfo = ppProcessInfo.getValue();
70              final WTS_PROCESS_INFO_EX processInfoRef = new WTS_PROCESS_INFO_EX(pProcessInfo);
71              WTS_PROCESS_INFO_EX[] processInfo = (WTS_PROCESS_INFO_EX[]) processInfoRef.toArray(pCount.getValue());
72              for (WTS_PROCESS_INFO_EX info : processInfo) {
73                  if (pids == null || pids.contains(info.ProcessId)) {
74                      wtsMap.put(info.ProcessId,
75                              new WtsInfo(info.pProcessName, "", info.NumberOfThreads, info.PagefileUsage & 0xffff_ffffL,
76                                      info.KernelTime.getValue() / 10_000L, info.UserTime.getValue() / 10_000,
77                                      info.HandleCount));
78                  }
79              }
80              // Clean up memory
81              if (!Wtsapi32.INSTANCE.WTSFreeMemoryEx(Wtsapi32.WTS_PROCESS_INFO_LEVEL_1, pProcessInfo,
82                      pCount.getValue())) {
83                  LOG.warn("Failed to Free Memory for Processes. Error code: {}", Kernel32.INSTANCE.GetLastError());
84              }
85          }
86          return wtsMap;
87      }
88  
89      private static Map<Integer, WtsInfo> queryProcessWtsMapFromPerfMon(Collection<Integer> pids) {
90          Map<Integer, WtsInfo> wtsMap = new HashMap<>();
91          WmiResult<ProcessXPProperty> processWmiResult = Win32Process.queryProcesses(pids);
92          for (int i = 0; i < processWmiResult.getResultCount(); i++) {
93              wtsMap.put(WmiUtil.getUint32(processWmiResult, ProcessXPProperty.PROCESSID, i), new WtsInfo(
94                      WmiUtil.getString(processWmiResult, ProcessXPProperty.NAME, i),
95                      WmiUtil.getString(processWmiResult, ProcessXPProperty.EXECUTABLEPATH, i),
96                      WmiUtil.getUint32(processWmiResult, ProcessXPProperty.THREADCOUNT, i),
97                      // WMI Pagefile usage is in KB
98                      1024 * (WmiUtil.getUint32(processWmiResult, ProcessXPProperty.PAGEFILEUSAGE, i) & 0xffff_ffffL),
99                      WmiUtil.getUint64(processWmiResult, ProcessXPProperty.KERNELMODETIME, i) / 10_000L,
100                     WmiUtil.getUint64(processWmiResult, ProcessXPProperty.USERMODETIME, i) / 10_000L,
101                     WmiUtil.getUint32(processWmiResult, ProcessXPProperty.HANDLECOUNT, i)));
102         }
103         return wtsMap;
104     }
105 
106     /**
107      * Class to encapsulate data from WTS Process Info
108      */
109     @Immutable
110     public static class WtsInfo {
111         private final String name;
112         private final String path;
113         private final int threadCount;
114         private final long virtualSize;
115         private final long kernelTime;
116         private final long userTime;
117         private final long openFiles;
118 
119         public WtsInfo(String name, String path, int threadCount, long virtualSize, long kernelTime, long userTime,
120                 long openFiles) {
121             this.name = name;
122             this.path = path;
123             this.threadCount = threadCount;
124             this.virtualSize = virtualSize;
125             this.kernelTime = kernelTime;
126             this.userTime = userTime;
127             this.openFiles = openFiles;
128         }
129 
130         /**
131          * @return the name
132          */
133         public String getName() {
134             return name;
135         }
136 
137         /**
138          * @return the path
139          */
140         public String getPath() {
141             return path;
142         }
143 
144         /**
145          * @return the threadCount
146          */
147         public int getThreadCount() {
148             return threadCount;
149         }
150 
151         /**
152          * @return the virtualSize
153          */
154         public long getVirtualSize() {
155             return virtualSize;
156         }
157 
158         /**
159          * @return the kernelTime
160          */
161         public long getKernelTime() {
162             return kernelTime;
163         }
164 
165         /**
166          * @return the userTime
167          */
168         public long getUserTime() {
169             return userTime;
170         }
171 
172         /**
173          * @return the openFiles
174          */
175         public long getOpenFiles() {
176             return openFiles;
177         }
178     }
179 }