View Javadoc
1   /*
2    * Copyright 2016-2025 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
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   * Microsoft Windows, commonly referred to as Windows, is a group of several proprietary graphical operating system
87   * families, all of which are developed and marketed by Microsoft.
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      * Windows event log name
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      * OSProcess code will need to know bitness of current process
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      * Cache full process stats queries. Second query will only populate if first one returns null.
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      * Cache full thread stats queries. Second query will only populate if first one returns null. Only used if
129      * USE_PROCSTATE_SUSPENDED is set true.
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         // Older JDKs don't recognize Win11 and Server2022
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      * Gets suites available on the system and return as a codename
179      *
180      * @param suiteMask The suite mask bitmask
181      *
182      * @return Suites
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         // Get processes from ToolHelp API for parent PID
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         // Get data from the registry if possible
298         Map<Integer, ProcessPerformanceData.PerfCounterBlock> processMap = processMapFromRegistry.get();
299         // otherwise performance counters with WMI backup
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             // Get data from the registry if possible
307             threadMap = threadMapFromRegistry.get();
308             // otherwise performance counters with WMI backup
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         // Uptime is in seconds so divide milliseconds
390         // GetTickCount64 requires Vista (6.0) or later
391         if (IS_VISTA_OR_GREATER) {
392             return Kernel32.INSTANCE.GetTickCount64() / 1000L;
393         } else {
394             // 32 bit rolls over at ~ 49 days
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                 // Get the most recent boot event (ID 12) from the Event log. If Windows "Fast
410                 // Startup" is enabled we may not see event 12, so also check for most recent ID
411                 // 6005 (Event log startup) as a reasonably close backup.
412                 long event6005Time = 0L;
413                 while (iter.hasNext()) {
414                     EventLogRecord logRecord = iter.next();
415                     if (logRecord.getStatusCode() == 12) {
416                         // Event 12 is system boot. We want this value unless we find two 6005 events
417                         // first (may occur with Fast Boot)
418                         return logRecord.getRecord().TimeGenerated.longValue();
419                     } else if (logRecord.getStatusCode() == 6005) {
420                         // If we already found one, this means we've found a second one without finding
421                         // an event 12. Return the latest one.
422                         if (event6005Time > 0) {
423                             return event6005Time;
424                         }
425                         // First 6005; tentatively assign
426                         event6005Time = logRecord.getRecord().TimeGenerated.longValue();
427                     }
428                 }
429                 // Only one 6005 found, return
430                 if (event6005Time > 0) {
431                     return event6005Time;
432                 }
433             } catch (Win32Exception e) {
434                 LOG.warn("Can't open event log \"{}\".", eventLog);
435             }
436         }
437         // If we get this far, event log reading has failed, either from no log or no
438         // startup times. Subtract up time from current time as a reasonable proxy.
439         return System.currentTimeMillis() / 1000L - querySystemUptime();
440     }
441 
442     @Override
443     public NetworkParams getNetworkParams() {
444         return new WindowsNetworkParams();
445     }
446 
447     /**
448      * Attempts to enable debug privileges for this process, required for OpenProcess() to get processes other than the
449      * current user. Requires elevated permissions.
450      *
451      * @return {@code true} if debug privileges were successfully enabled.
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             // Use faster boot time approximation
519             return null;
520         }
521         // Check whether it works
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      * Package-private methods for use by WindowsOSProcess to limit process memory queries to processes with same
543      * bitness as the current one
544      */
545     /**
546      * Is the processor architecture x86?
547      *
548      * @return true if the processor architecture is Intel x86
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      * Is the current operating process x86 or x86-compatibility mode?
563      *
564      * @return true if the current process is 32-bit
565      */
566     static boolean isWow() {
567         return WOW;
568     }
569 
570     /**
571      * Is the specified process x86 or x86-compatibility mode?
572      *
573      * @param h The handle to the processs to check
574      * @return true if the process is 32-bit
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 }