1
2
3
4
5 package oshi.driver.windows;
6
7 import static com.sun.jna.platform.win32.Cfgmgr32.CM_DRP_CLASS;
8 import static com.sun.jna.platform.win32.Cfgmgr32.CM_DRP_DEVICEDESC;
9 import static com.sun.jna.platform.win32.Cfgmgr32.CM_DRP_FRIENDLYNAME;
10 import static com.sun.jna.platform.win32.Cfgmgr32.CM_DRP_MFG;
11 import static com.sun.jna.platform.win32.Cfgmgr32.CM_DRP_SERVICE;
12 import static com.sun.jna.platform.win32.SetupApi.DIGCF_DEVICEINTERFACE;
13 import static com.sun.jna.platform.win32.SetupApi.DIGCF_PRESENT;
14 import static com.sun.jna.platform.win32.WinBase.INVALID_HANDLE_VALUE;
15 import static com.sun.jna.platform.win32.WinError.ERROR_SUCCESS;
16
17 import java.util.ArrayDeque;
18 import java.util.HashMap;
19 import java.util.Map;
20 import java.util.Queue;
21 import java.util.Set;
22 import java.util.stream.Collectors;
23
24 import com.sun.jna.Memory;
25 import com.sun.jna.platform.win32.Cfgmgr32;
26 import com.sun.jna.platform.win32.Cfgmgr32Util;
27 import com.sun.jna.platform.win32.Guid.GUID;
28 import com.sun.jna.platform.win32.SetupApi;
29 import com.sun.jna.platform.win32.WinNT.HANDLE;
30 import com.sun.jna.ptr.IntByReference;
31
32 import oshi.annotation.concurrent.ThreadSafe;
33 import oshi.jna.ByRef.CloseableIntByReference;
34 import oshi.jna.Struct.CloseableSpDevinfoData;
35 import oshi.util.tuples.Quintet;
36
37
38
39
40 @ThreadSafe
41 public final class DeviceTree {
42
43 private static final int MAX_PATH = 260;
44 private static final SetupApi SA = SetupApi.INSTANCE;
45 private static final Cfgmgr32 C32 = Cfgmgr32.INSTANCE;
46
47 private DeviceTree() {
48 }
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 public static Quintet<Set<Integer>, Map<Integer, Integer>, Map<Integer, String>, Map<Integer, String>, Map<Integer, String>> queryDeviceTree(
64 GUID guidDevInterface) {
65 Map<Integer, Integer> parentMap = new HashMap<>();
66 Map<Integer, String> nameMap = new HashMap<>();
67 Map<Integer, String> deviceIdMap = new HashMap<>();
68 Map<Integer, String> mfgMap = new HashMap<>();
69
70 HANDLE hDevInfo = SA.SetupDiGetClassDevs(guidDevInterface, null, null, DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
71 if (!INVALID_HANDLE_VALUE.equals(hDevInfo)) {
72 try (Memory buf = new Memory(MAX_PATH);
73 CloseableIntByReference size = new CloseableIntByReference(MAX_PATH);
74 CloseableIntByReference child = new CloseableIntByReference();
75 CloseableIntByReference sibling = new CloseableIntByReference();
76 CloseableSpDevinfoData devInfoData = new CloseableSpDevinfoData()) {
77 devInfoData.cbSize = devInfoData.size();
78
79 Queue<Integer> deviceTree = new ArrayDeque<>();
80 for (int i = 0; SA.SetupDiEnumDeviceInfo(hDevInfo, i, devInfoData); i++) {
81 deviceTree.add(devInfoData.DevInst);
82
83 int node = 0;
84 while (!deviceTree.isEmpty()) {
85
86 node = deviceTree.poll();
87
88
89 String deviceId = Cfgmgr32Util.CM_Get_Device_ID(node);
90 deviceIdMap.put(node, deviceId);
91
92
93 String name = getDevNodeProperty(node, CM_DRP_FRIENDLYNAME, buf, size);
94 if (name.isEmpty()) {
95 name = getDevNodeProperty(node, CM_DRP_DEVICEDESC, buf, size);
96 }
97 if (name.isEmpty()) {
98 name = getDevNodeProperty(node, CM_DRP_CLASS, buf, size);
99 String svc = getDevNodeProperty(node, CM_DRP_SERVICE, buf, size);
100 if (!svc.isEmpty()) {
101 name = name + " (" + svc + ")";
102 }
103 }
104 nameMap.put(node, name);
105 mfgMap.put(node, getDevNodeProperty(node, CM_DRP_MFG, buf, size));
106
107
108 if (ERROR_SUCCESS == C32.CM_Get_Child(child, node, 0)) {
109 parentMap.put(child.getValue(), node);
110 deviceTree.add(child.getValue());
111 while (ERROR_SUCCESS == C32.CM_Get_Sibling(sibling, child.getValue(), 0)) {
112 parentMap.put(sibling.getValue(), node);
113 deviceTree.add(sibling.getValue());
114 child.setValue(sibling.getValue());
115 }
116 }
117 }
118 }
119 } finally {
120 SA.SetupDiDestroyDeviceInfoList(hDevInfo);
121 }
122 }
123
124 Set<Integer> controllerDevices = deviceIdMap.keySet().stream().filter(k -> !parentMap.containsKey(k))
125 .collect(Collectors.toSet());
126 return new Quintet<>(controllerDevices, parentMap, nameMap, deviceIdMap, mfgMap);
127 }
128
129 private static String getDevNodeProperty(int node, int cmDrp, Memory buf, IntByReference size) {
130 buf.clear();
131 size.setValue((int) buf.size());
132 C32.CM_Get_DevNode_Registry_Property(node, cmDrp, null, buf, size, 0);
133 return buf.getWideString(0);
134 }
135 }