├── .gitattributes
├── .gitignore
├── GoInterruptPolicy.manifest
├── README.md
├── SaveBackup.go
├── app.go
├── build.bat
├── calculateLayout.go
├── cpuid.go
├── cpusetinformation.go
├── cpusetinformation_fake.go
├── dialog.go
├── flags.go
├── go.mod
├── go.sum
├── guid.go
├── init.go
├── kernel32.go
├── ntdll.go
├── reg.go
├── rsrc.syso
├── run.bat
├── setupapi_windows.go
├── types_windows.go
├── winapi.go
└── zsetupapi_windows.go
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.exe
2 | *.zip
3 | *.tmp
4 |
--------------------------------------------------------------------------------
/GoInterruptPolicy.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | PerMonitorV2, PerMonitor
12 | True
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GoInterruptPolicy
2 | [![Downloads][1]][2] [![GitHub stars][3]][4]
3 |
4 | [1]: https://img.shields.io/github/downloads/spddl/GoInterruptPolicy/total.svg
5 | [2]: https://github.com/spddl/GoInterruptPolicy/releases "Downloads"
6 |
7 | [3]: https://img.shields.io/github/stars/spddl/GoInterruptPolicy.svg
8 | [4]: https://github.com/spddl/GoInterruptPolicy/stargazers "GitHub stars"
9 |
10 | I just tried to create a better version of the "Interrupt Affinity Policy" and "MSI Mode" Tool
11 |
12 | 
13 |
--------------------------------------------------------------------------------
/SaveBackup.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "log"
7 | "strings"
8 | "text/template"
9 |
10 | "github.com/tailscale/walk"
11 | )
12 |
13 | func createRegFile(regpath string, item *Device) string {
14 | packageInfo := template.New("packageInfo")
15 | tmplProperty := template.Must(packageInfo.Parse(string(`Windows Registry Editor Version 5.00
16 |
17 | [{{.RegPath}}\Interrupt Management]
18 |
19 | [{{.RegPath}}\Interrupt Management\Affinity Policy]
20 | "DevicePolicy"=dword:{{printf "%08d" .Device.DevicePolicy}}
21 | {{if eq .Device.DevicePriority 0}}"DevicePriority"=-{{else}}"DevicePriority"=dword:{{printf "%08d" .Device.DevicePriority}}{{end}}
22 | {{if ne .Device.DevicePolicy 4}}"AssignmentSetOverride"=-{{else}}"AssignmentSetOverride"=hex:{{.AssignmentSetOverride}}{{end}}
23 |
24 | [{{.RegPath}}\Interrupt Management\MessageSignaledInterruptProperties]
25 | "MSISupported"=dword:{{printf "%08d" .Device.MsiSupported}}
26 | {{if eq .Device.MsiSupported 1}}{{if ne .Device.MessageNumberLimit 0}}"MessageNumberLimit"=dword:{{printf "%08d" .Device.MessageNumberLimit}}{{end}}{{else}}"MessageNumberLimit"=-{{end}}
27 | `)))
28 |
29 | var buf bytes.Buffer
30 | err := tmplProperty.Execute(&buf, struct {
31 | RegPath string
32 | Device Device
33 | AssignmentSetOverride string
34 | }{
35 | regpath,
36 | *item,
37 | addComma(fmt.Sprintf("%x", item.AssignmentSetOverride)),
38 | })
39 |
40 | if err != nil {
41 | log.Fatalln(err)
42 | }
43 |
44 | return strings.ReplaceAll(buf.String(), "\n", "\r\n")
45 |
46 | }
47 |
48 | func addComma(data string) string {
49 | var b strings.Builder
50 | for i := 0; i < len(data); i++ {
51 | if i != 0 && i%2 == 0 {
52 | b.WriteString(",")
53 | }
54 | b.WriteString(string(data[i]))
55 | }
56 |
57 | return b.String()
58 | }
59 |
60 | func saveFileExplorer(owner walk.Form, path, filename, title, filter string) (filePath string, cancel bool, err error) {
61 | dlg := new(walk.FileDialog)
62 |
63 | dlg.Title = title
64 | dlg.InitialDirPath = path
65 | dlg.Filter = filter
66 | dlg.FilePath = filename
67 |
68 | ok, err := dlg.ShowSave(owner)
69 | if err != nil {
70 | return "", !ok, err
71 | } else if !ok {
72 | return "", !ok, nil
73 | }
74 |
75 | return dlg.FilePath, !ok, nil
76 | }
77 |
--------------------------------------------------------------------------------
/app.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "os"
7 | "sort"
8 | "strconv"
9 | "strings"
10 | "time"
11 | "unsafe"
12 |
13 | "github.com/tailscale/walk"
14 |
15 | //lint:ignore ST1001 standard behavior tailscale/walk
16 | . "github.com/tailscale/walk/declarative"
17 | )
18 |
19 | var cs CpuSets
20 |
21 | func main() {
22 | cs.Init()
23 |
24 | var devices []Device
25 | devices, handle = FindAllDevices()
26 |
27 | if CLIMode {
28 | var newItem *Device
29 | for i := 0; i < len(devices); i++ {
30 | if devices[i].DevObjName == flagDevObjName {
31 | newItem = &devices[i]
32 | break
33 | }
34 | }
35 | if newItem == nil {
36 | SetupDiDestroyDeviceInfoList(handle)
37 | os.Exit(1)
38 | }
39 | orgItem := *newItem
40 |
41 | var assignmentSetOverride Bits
42 | if flagCPU != "" {
43 | cpuarray := strings.Split(flagCPU, ",")
44 | for _, val := range cpuarray {
45 | i, err := strconv.Atoi(val)
46 | if err != nil {
47 | log.Println(err)
48 | continue
49 | }
50 | assignmentSetOverride = Set(assignmentSetOverride, CPUBits[i])
51 | }
52 | }
53 |
54 | if flagMsiSupported != -1 || flagMessageNumberLimit != -1 {
55 | if flagMsiSupported != -1 {
56 | newItem.MsiSupported = uint32(flagMsiSupported)
57 | }
58 | if flagMessageNumberLimit != -1 {
59 | newItem.MessageNumberLimit = uint32(flagMessageNumberLimit)
60 | }
61 | setMSIMode(newItem)
62 | }
63 |
64 | if flagDevicePolicy != -1 || flagDevicePriority != -1 || assignmentSetOverride != ZeroBit {
65 | if flagDevicePolicy != -1 {
66 | newItem.DevicePolicy = uint32(flagDevicePolicy)
67 | }
68 | if flagDevicePriority != -1 {
69 | newItem.DevicePriority = uint32(flagDevicePriority)
70 | }
71 | if assignmentSetOverride != ZeroBit {
72 | newItem.AssignmentSetOverride = assignmentSetOverride
73 | }
74 | setAffinityPolicy(newItem)
75 | }
76 |
77 | changed := orgItem.MsiSupported != newItem.MsiSupported || orgItem.MessageNumberLimit != newItem.MessageNumberLimit || orgItem.DevicePolicy != newItem.DevicePolicy || orgItem.DevicePriority != newItem.DevicePriority || orgItem.AssignmentSetOverride != newItem.AssignmentSetOverride
78 | if flagRestart || (flagRestartOnChange && changed) {
79 | propChangeParams := PropChangeParams{
80 | ClassInstallHeader: *MakeClassInstallHeader(DIF_PROPERTYCHANGE),
81 | StateChange: DICS_PROPCHANGE,
82 | Scope: DICS_FLAG_GLOBAL,
83 | }
84 |
85 | if err := SetupDiSetClassInstallParams(handle, &newItem.Idata, &propChangeParams.ClassInstallHeader, uint32(unsafe.Sizeof(propChangeParams))); err != nil {
86 | log.Println(err)
87 | return
88 | }
89 |
90 | if err := SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, handle, &newItem.Idata); err != nil {
91 | log.Println(err)
92 | return
93 | }
94 |
95 | if err := SetupDiSetClassInstallParams(handle, &newItem.Idata, &propChangeParams.ClassInstallHeader, uint32(unsafe.Sizeof(propChangeParams))); err != nil {
96 | log.Println(err)
97 | return
98 | }
99 |
100 | if err := SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, handle, &newItem.Idata); err != nil {
101 | log.Println(err)
102 | return
103 | }
104 |
105 | DeviceInstallParams, err := SetupDiGetDeviceInstallParams(handle, &newItem.Idata)
106 | if err != nil {
107 | log.Println(err)
108 | return
109 | }
110 |
111 | if DeviceInstallParams.Flags&DI_NEEDREBOOT != 0 { // DI_NEEDREBOOT
112 | fmt.Println("Device could not be restarted. Changes will take effect the next time you reboot.")
113 | } else {
114 | fmt.Println("Device successfully restarted.")
115 | }
116 | }
117 | SetupDiDestroyDeviceInfoList(handle)
118 | os.Exit(0)
119 | }
120 | defer SetupDiDestroyDeviceInfoList(handle)
121 |
122 | // Sortiert das Array nach Namen
123 | sort.Slice(devices, func(i, j int) bool {
124 | return devices[i].DeviceDesc < devices[j].DeviceDesc
125 | })
126 |
127 | AllDevices := devices
128 |
129 | var LineEditSearch *walk.LineEdit
130 | mw := &MyMainWindow{
131 | model: &Model{items: devices},
132 | tv: &walk.TableView{},
133 | }
134 | if err := (MainWindow{
135 | AssignTo: &mw.MainWindow,
136 | Title: "GoInterruptPolicy",
137 | MinSize: Size{
138 | Width: 240,
139 | Height: 320,
140 | },
141 | Size: Size{
142 | Width: 750,
143 | Height: 600,
144 | },
145 | Layout: VBox{
146 | MarginsZero: true,
147 | SpacingZero: true,
148 | },
149 | Children: []Widget{
150 | Composite{
151 | Layout: VBox{},
152 | Children: []Widget{
153 | LineEdit{
154 | AssignTo: &LineEditSearch,
155 | CueBanner: "Search",
156 | OnTextChanged: func() {
157 | text := strings.ToLower(LineEditSearch.Text())
158 | if text == "" {
159 | mw.tv.SetModel(&Model{items: AllDevices})
160 | mw.sbi.SetText(fmt.Sprintf("%d Devices Found", len(devices)))
161 | } else {
162 | newDevices := []Device{}
163 | for i := 0; i < len(AllDevices); i++ {
164 | if strings.Contains(strings.ToLower(AllDevices[i].DeviceDesc), text) ||
165 | strings.Contains(strings.ToLower(AllDevices[i].DevObjName), text) ||
166 | strings.Contains(strings.ToLower(AllDevices[i].LocationInformation), text) ||
167 | strings.Contains(strings.ToLower(AllDevices[i].FriendlyName), text) {
168 | newDevices = append(newDevices, AllDevices[i])
169 | }
170 | }
171 | mw.tv.SetModel(&Model{items: newDevices})
172 | mw.sbi.SetText(fmt.Sprintf("%d Devices Found", len(newDevices)))
173 | }
174 | },
175 | },
176 | },
177 | },
178 | TableView{
179 | OnItemActivated: mw.lb_ItemActivated,
180 | Name: "tableView", // Name is needed for settings persistence
181 | AlternatingRowBG: true,
182 | ColumnsOrderable: true,
183 | ColumnsSizable: true,
184 | LastColumnStretched: true,
185 |
186 | Model: mw.model,
187 | AssignTo: &mw.tv,
188 | OnKeyUp: func(key walk.Key) {
189 | i := mw.tv.CurrentIndex()
190 | if i == -1 {
191 | i = 0
192 | }
193 | for ; i < len(mw.model.items); i++ {
194 | item := &mw.model.items[i]
195 | if item.DeviceDesc != "" && key.String() == item.DeviceDesc[0:1] {
196 | err := mw.tv.SetCurrentIndex(i)
197 | if err != nil {
198 | log.Println(err)
199 | }
200 | return
201 | }
202 | }
203 | },
204 | Columns: []TableViewColumn{
205 | {
206 | Name: "DeviceDesc",
207 | Title: "Name",
208 | Width: 150,
209 | },
210 | {
211 | Name: "FriendlyName",
212 | Title: "Friendly Name",
213 | },
214 | {
215 | Name: "LocationInformation",
216 | Title: "Location Info",
217 | Width: 150,
218 | },
219 | {
220 | Name: "MsiSupported",
221 | Title: "MSI Mode",
222 | Width: 60,
223 | Alignment: AlignCenter,
224 | FormatFunc: func(value interface{}) string {
225 | if value.(uint32) == 0 {
226 | return "✖"
227 | } else if value.(uint32) == 1 {
228 | return "✔"
229 | }
230 | return ""
231 | },
232 | },
233 | {
234 | Name: "DevicePolicy",
235 | Title: "Device Policy",
236 | FormatFunc: func(value interface{}) string {
237 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/interrupt-affinity-and-priority
238 | switch value.(uint32) {
239 | case IrqPolicyMachineDefault: // 0x00
240 | return "Default"
241 | case IrqPolicyAllCloseProcessors: // 0x01
242 | return "All Close Proc"
243 | case IrqPolicyOneCloseProcessor: // 0x02
244 | return "One Close Proc"
245 | case IrqPolicyAllProcessorsInMachine: // 0x03
246 | return "All Proc in Machine"
247 | case IrqPolicySpecifiedProcessors: // 0x04
248 | return "Specified Proc"
249 | case IrqPolicySpreadMessagesAcrossAllProcessors: // 0x05
250 | return "Spread Messages Across All Proc"
251 | default:
252 | return fmt.Sprintf("%d", value.(uint32))
253 | }
254 | },
255 | },
256 | {
257 | Name: "AssignmentSetOverride",
258 | Title: "Specified Processor",
259 | FormatFunc: func(value interface{}) string {
260 | if value == ZeroBit {
261 | return ""
262 | }
263 | bits := value.(Bits)
264 | var result []string
265 | for bit, cpu := range CPUMap {
266 | if Has(bit, bits) {
267 | result = append(result, cpu)
268 | }
269 | }
270 |
271 | result, err := sortNumbers(result)
272 | if err != nil {
273 | log.Println(err)
274 | }
275 | return strings.Join(result, ",")
276 | },
277 | LessFunc: func(i, j int) bool {
278 | return mw.model.items[i].AssignmentSetOverride < mw.model.items[j].AssignmentSetOverride
279 | },
280 | },
281 | {
282 | Name: "DevicePriority",
283 | Title: "Device Priority",
284 | FormatFunc: func(value interface{}) string {
285 | switch value.(uint32) {
286 | case 0:
287 | return "Undefined"
288 | case 1:
289 | return "Low"
290 | case 2:
291 | return "Normal"
292 | case 3:
293 | return "High"
294 | default:
295 | return fmt.Sprintf("%d", value.(uint32))
296 | }
297 | },
298 | },
299 | {
300 | Name: "InterruptTypeMap",
301 | Title: "Interrupt Type",
302 | Width: 120,
303 | FormatFunc: func(value interface{}) string {
304 | return interruptType(value.(Bits))
305 | },
306 | LessFunc: func(i, j int) bool {
307 | return mw.model.items[i].InterruptTypeMap < mw.model.items[j].InterruptTypeMap
308 | },
309 | },
310 | {
311 | Name: "MessageNumberLimit",
312 | Title: "MSI Limit",
313 | FormatFunc: func(value interface{}) string {
314 | switch value.(uint32) {
315 | case 0:
316 | return ""
317 | default:
318 | return fmt.Sprintf("%d", value.(uint32))
319 | }
320 | },
321 | },
322 | {
323 | Name: "MaxMSILimit",
324 | Title: "Max MSI Limit",
325 | FormatFunc: func(value interface{}) string {
326 | switch value.(uint32) {
327 | case 0:
328 | return ""
329 | default:
330 | return fmt.Sprintf("%d", value.(uint32))
331 | }
332 | },
333 | },
334 | {
335 | Name: "DevObjName",
336 | Title: "DevObj Name",
337 | },
338 | {
339 | Name: "LastChange",
340 | Title: "Last Change",
341 | Width: 130,
342 | FormatFunc: func(value interface{}) string {
343 | if value.(time.Time).IsZero() {
344 | return "N/A"
345 | } else {
346 | return value.(time.Time).Format("2006-01-02 15:04:05")
347 | }
348 | },
349 | },
350 | },
351 | },
352 | },
353 | StatusBarItems: []StatusBarItem{
354 | {
355 | AssignTo: &mw.sbi,
356 | Text: fmt.Sprintf("%d Devices Found", len(devices)),
357 | },
358 | },
359 | }).Create(); err != nil {
360 | log.Println(err)
361 | return
362 | }
363 |
364 | var maxDeviceDesc int
365 | for i := range devices {
366 | newDeviceDesc := mw.TextWidthSize(devices[i].DeviceDesc)
367 | if maxDeviceDesc < newDeviceDesc {
368 | maxDeviceDesc = newDeviceDesc
369 | }
370 | }
371 | if maxDeviceDesc < 150 {
372 | mw.tv.Columns().At(0).SetWidth(maxDeviceDesc)
373 | }
374 |
375 | mw.Show()
376 | mw.tv.SetFocus()
377 | mw.Run()
378 | }
379 |
380 | type MyMainWindow struct {
381 | *walk.MainWindow
382 | tv *walk.TableView
383 | model *Model
384 | sbi *walk.StatusBarItem
385 | }
386 |
387 | func (mw *MyMainWindow) lb_ItemActivated() {
388 | newItem := &mw.tv.Model().(*Model).items[mw.tv.CurrentIndex()]
389 | orgItem := *newItem
390 | result, err := RunDialog(mw, newItem)
391 | if err != nil {
392 | log.Print(err)
393 | }
394 | if result == 0 || result == 2 { // cancel
395 | mw.tv.Model().(*Model).items[mw.tv.CurrentIndex()] = orgItem
396 | return
397 | }
398 |
399 | if orgItem.MsiSupported != newItem.MsiSupported || orgItem.MessageNumberLimit != newItem.MessageNumberLimit {
400 | setMSIMode(newItem)
401 | }
402 |
403 | if orgItem.DevicePolicy != newItem.DevicePolicy || orgItem.DevicePriority != newItem.DevicePriority || orgItem.AssignmentSetOverride != newItem.AssignmentSetOverride {
404 | setAffinityPolicy(newItem)
405 | }
406 |
407 | if orgItem.MsiSupported != newItem.MsiSupported || orgItem.MessageNumberLimit != newItem.MessageNumberLimit || orgItem.DevicePolicy != newItem.DevicePolicy || orgItem.DevicePriority != newItem.DevicePriority || orgItem.AssignmentSetOverride != newItem.AssignmentSetOverride {
408 | if walk.MsgBox(mw.WindowBase.Form(), "Restart Device?", `Your changes will not take effect until the device is restarted.
409 |
410 | Would you like to attempt to restart the device now?`, walk.MsgBoxYesNo) == 6 {
411 | propChangeParams := PropChangeParams{
412 | ClassInstallHeader: *MakeClassInstallHeader(DIF_PROPERTYCHANGE),
413 | StateChange: DICS_PROPCHANGE,
414 | Scope: DICS_FLAG_GLOBAL,
415 | }
416 |
417 | if err := SetupDiSetClassInstallParams(handle, &newItem.Idata, &propChangeParams.ClassInstallHeader, uint32(unsafe.Sizeof(propChangeParams))); err != nil {
418 | log.Println(err)
419 | return
420 | }
421 |
422 | if err := SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, handle, &newItem.Idata); err != nil {
423 | log.Println(err)
424 | return
425 | }
426 |
427 | if err := SetupDiSetClassInstallParams(handle, &newItem.Idata, &propChangeParams.ClassInstallHeader, uint32(unsafe.Sizeof(propChangeParams))); err != nil {
428 | log.Println(err)
429 | return
430 | }
431 |
432 | if err := SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, handle, &newItem.Idata); err != nil {
433 | log.Println(err)
434 | return
435 | }
436 |
437 | DeviceInstallParams, err := SetupDiGetDeviceInstallParams(handle, &newItem.Idata)
438 | if err != nil {
439 | log.Println(err)
440 | return
441 | }
442 |
443 | if DeviceInstallParams.Flags&DI_NEEDREBOOT != 0 {
444 | walk.MsgBox(mw.WindowBase.Form(), "Notice", "Device could not be restarted. Changes will take effect the next time you reboot.", walk.MsgBoxOK)
445 | } else {
446 | walk.MsgBox(mw.WindowBase.Form(), "Notice", "Device successfully restarted.", walk.MsgBoxOK)
447 | }
448 |
449 | } else {
450 | mw.sbi.SetText("Restart required")
451 | }
452 | }
453 | }
454 |
455 | func (mw *MyMainWindow) TextWidthSize(text string) int {
456 | canvas, err := (*mw.tv).CreateCanvas()
457 | if err != nil {
458 | return 0
459 | }
460 | defer canvas.Dispose()
461 |
462 | bounds, _, err := canvas.MeasureTextPixels(text, (*mw.tv).Font(), walk.Rectangle{Width: 9999999}, walk.TextCalcRect)
463 | if err != nil {
464 | return 0
465 | }
466 |
467 | return bounds.Size().Width
468 | }
469 |
470 | type Model struct {
471 | // walk.SortedReflectTableModelBase
472 | items []Device
473 | }
474 |
475 | func (m *Model) Items() interface{} {
476 | return m.items
477 | }
478 |
479 | func sortNumbers(data []string) ([]string, error) {
480 | var lastErr error
481 | sort.Slice(data, func(i, j int) bool {
482 | a, err := strconv.ParseInt(data[i], 10, 64)
483 | if err != nil {
484 | lastErr = err
485 | return false
486 | }
487 | b, err := strconv.ParseInt(data[j], 10, 64)
488 | if err != nil {
489 | lastErr = err
490 | return false
491 | }
492 | return a < b
493 | })
494 | return data, lastErr
495 | }
496 |
--------------------------------------------------------------------------------
/build.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | SET GOOS=windows
4 | SET GOARCH=amd64
5 | SET filename=GoInterruptPolicy
6 |
7 | :loop
8 | CLS
9 |
10 | gocritic check -enableAll -disable="#experimental,#opinionated,#commentedOutCode" ./...
11 |
12 | IF exist %filename%.exe (
13 | FOR /F "usebackq" %%A IN ('%filename%.exe') DO SET /A beforeSize=%%~zA
14 | ) ELSE (
15 | SET /A beforeSize=0
16 | )
17 |
18 | : Build https://golang.org/cmd/go/
19 | go build -tags debug -buildvcs=false -o %filename%_debug.exe
20 | go build -ldflags="-w -s -H windowsgui" -o %filename%.exe
21 |
22 | FOR /F "usebackq" %%A IN ('%filename%.exe') DO SET /A size=%%~zA
23 | SET /A diffSize = %size% - %beforeSize%
24 | SET /A size=(%size%/1024)+1
25 | IF %diffSize% EQU 0 (
26 | ECHO %size% kb
27 | ) ELSE (
28 | IF %diffSize% GTR 0 (
29 | ECHO %size% kb [+%diffSize% b]
30 | ) ELSE (
31 | ECHO %size% kb [%diffSize% b]
32 | )
33 | )
34 |
35 | : Run
36 | @REM IF %ERRORLEVEL% EQU 0 %filename%.exe
37 |
38 | PAUSE
39 | GOTO loop
40 |
--------------------------------------------------------------------------------
/calculateLayout.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "math"
5 | )
6 |
7 | func mathCeilInInt(n, d int) int {
8 | if n%d == 0 {
9 | return n / d
10 | }
11 | return (n / d) + 1
12 | }
13 |
14 | func getLayout(groups ...int) (rows int, columns []int) {
15 | maxRows := 0
16 | for _, count := range groups {
17 | if count > maxRows {
18 | maxRows = count
19 | }
20 | }
21 |
22 | bestDiff := math.MaxFloat64
23 | bestRows := 0
24 | bestColumns := make([]int, len(groups))
25 |
26 | for r := 1; r <= maxRows; r++ {
27 | currentColumns := make([]int, len(groups))
28 | totalCols := 0
29 |
30 | for i, count := range groups {
31 | col := mathCeilInInt(count, r)
32 | currentColumns[i] = col
33 | totalCols += col
34 | }
35 |
36 | diff := math.Abs(float64(totalCols*9 - r*16))
37 |
38 | if diff < bestDiff {
39 | bestDiff = diff
40 | bestRows = r
41 | copy(bestColumns, currentColumns)
42 | }
43 | }
44 |
45 | return bestRows, bestColumns
46 | }
47 |
--------------------------------------------------------------------------------
/cpuid.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import "github.com/intel-go/cpuid"
4 |
5 | func isAMD() bool {
6 | return cpuid.VendorIdentificatorString == "AuthenticAMD"
7 | }
8 |
9 | func isIntel() bool {
10 | return cpuid.VendorIdentificatorString == "GenuineIntel"
11 | }
12 |
--------------------------------------------------------------------------------
/cpusetinformation.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 |
6 | "golang.org/x/sys/windows"
7 | )
8 |
9 | var SystemCpuSets = []SYSTEM_CPU_SET_INFORMATION{}
10 |
11 | const (
12 | ToolTipTextNumaNode = "A group-relative value indicating which NUMA node a CPU Set is on. All CPU Sets in a given group that are on the same NUMA node will have the same value for this field."
13 | ToolTipTextLastLevelCache = "A group-relative value indicating which CPU Sets share at least one level of cache with each other. This value is the same for all CPU Sets in a group that are on processors that share cache with each other."
14 | ToolTipTextEfficiencyClass = "A value indicating the intrinsic energy efficiency of a processor for systems that support heterogeneous processors (such as ARM big.LITTLE systems). CPU Sets with higher numerical values of this field have home processors that are faster but less power-efficient than ones with lower values."
15 | )
16 |
17 | type CoreLayout struct {
18 | Rows int
19 | Cols int
20 | }
21 |
22 | type CpuSets struct {
23 | HyperThreading bool
24 | CoreCount int
25 | MaxThreadsPerCore int
26 | NumaNode bool // A group-relative value indicating which NUMA node a CPU Set is on. All CPU Sets in a given group that are on the same NUMA node will have the same value for this field.
27 | LastLevelCache bool // A group-relative value indicating which CPU Sets share at least one level of cache with each other. This value is the same for all CPU Sets in a group that are on processors that share cache with each other.
28 | EfficiencyClass bool // A value indicating the intrinsic energy efficiency of a processor for systems that support heterogeneous processors (such as ARM big.LITTLE systems). CPU Sets with higher numerical values of this field have home processors that are faster but less power-efficient than ones with lower values.
29 | CPU []CpuSet
30 | Layout []CoreLayout
31 | }
32 |
33 | type CpuSet struct {
34 | Id uint32
35 | CoreIndex byte
36 | LogicalProcessorIndex byte
37 | LastLevelCacheIndex byte // A group-relative value indicating which CPU Sets share at least one level of cache with each other. This value is the same for all CPU Sets in a group that are on processors that share cache with each other.
38 | EfficiencyClass byte // A value indicating the intrinsic energy efficiency of a processor for systems that support heterogeneous processors (such as ARM big.LITTLE systems). CPU Sets with higher numerical values of this field have home processors that are faster but less power-efficient than ones with lower values.
39 | NumaNodeIndex byte // A group-relative value indicating which NUMA node a CPU Set is on. All CPU Sets in a given group that are on the same NUMA node will have the same value for this field.
40 | }
41 |
42 | func (cs *CpuSets) Init() {
43 | size := 0x20 * 64
44 | SystemCpuSets = make([]SYSTEM_CPU_SET_INFORMATION, size)
45 |
46 | var length uint32
47 | var hProcess windows.Handle
48 | success := GetSystemCpuSetInformation(&SystemCpuSets[0], uint32(size), &length, uintptr(hProcess), 0)
49 | if success != 1 {
50 | log.Println("err")
51 | } else {
52 | SystemCpuSets = SystemCpuSets[:length]
53 | }
54 |
55 | /// debug
56 | // Fake13900()
57 | // Fake13900WithoutHT()
58 | // Fake5900x()
59 | // Fake8Threads()
60 | // FakeNumaCCD12Core()
61 | // Fake2CCD12CoreHT()
62 | // Fake13600KF()
63 |
64 | cs.CoreCount = int(uint32(len(SystemCpuSets)) / SystemCpuSets[0].Size)
65 | var lastEfficiencyClass, lastLevelCache, lastNumaNodeIndex byte
66 | var LogicalCores int
67 | var ClassGroup = []int{}
68 | for i := 0; i < cs.CoreCount; i++ {
69 | cpu := SystemCpuSets[i].CpuSet()
70 | if i == 0 { // The EfficiencyClass starts with 1 on the Intel Gen12+
71 | lastEfficiencyClass = cpu.EfficiencyClass
72 | }
73 |
74 | cs.CPU = append(cs.CPU, CpuSet{
75 | Id: cpu.Id,
76 | CoreIndex: cpu.CoreIndex,
77 | LogicalProcessorIndex: cpu.LogicalProcessorIndex,
78 | EfficiencyClass: cpu.EfficiencyClass,
79 | LastLevelCacheIndex: cpu.LastLevelCacheIndex,
80 | NumaNodeIndex: cpu.NumaNodeIndex,
81 | })
82 |
83 | // fmt.Printf("(%02d) [%d/%x] %02d/%02d Eff%d CCD%d NUMA%d\n", i, cpu.Id, cpu.Id, cpu.CoreIndex, cpu.LogicalProcessorIndex, cpu.EfficiencyClass, cpu.LastLevelCacheIndex, cpu.NumaNodeIndex)
84 |
85 | if cpu.CoreIndex != cpu.LogicalProcessorIndex {
86 | if !cs.HyperThreading {
87 | cs.HyperThreading = true
88 | }
89 | if cs.MaxThreadsPerCore < int(cpu.LogicalProcessorIndex-cpu.CoreIndex) {
90 | cs.MaxThreadsPerCore = int(cpu.LogicalProcessorIndex - cpu.CoreIndex)
91 | }
92 | } else {
93 | LogicalCores++
94 |
95 | for len(ClassGroup) <= int(cpu.EfficiencyClass) {
96 | ClassGroup = append(ClassGroup, 0)
97 | }
98 | ClassGroup[int(cpu.EfficiencyClass)]++
99 | }
100 |
101 | if !cs.EfficiencyClass && lastEfficiencyClass != cpu.EfficiencyClass {
102 | cs.EfficiencyClass = true
103 | }
104 |
105 | if !cs.LastLevelCache && lastLevelCache != cpu.LastLevelCacheIndex {
106 | cs.LastLevelCache = true
107 | }
108 |
109 | if !cs.NumaNode && lastNumaNodeIndex != cpu.NumaNodeIndex {
110 | cs.NumaNode = true
111 | }
112 |
113 | lastEfficiencyClass = cpu.EfficiencyClass
114 | lastLevelCache = cpu.LastLevelCacheIndex
115 | lastNumaNodeIndex = cpu.NumaNodeIndex
116 | }
117 |
118 | rows, cols := getLayout(ClassGroup...)
119 | for _, col := range cols {
120 | cs.Layout = append(cs.Layout, CoreLayout{
121 | Rows: rows,
122 | Cols: col,
123 | })
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/cpusetinformation_fake.go:
--------------------------------------------------------------------------------
1 | //go:build debug
2 |
3 | package main
4 |
5 | import (
6 | "log"
7 | )
8 |
9 | // AMD 5900x
10 | func Fake5900x() {
11 | log.Println("AMD 5900x")
12 | size := 0x20 * 64
13 | cpuSet := make([]SYSTEM_CPU_SET_INFORMATION, size)
14 | cpuSet[0].Size = 32
15 | var lastCoreIndex byte
16 | var count = 768
17 | var index = 0x100
18 | for i := 0; i < count; i++ {
19 | cs := cpuSet[i].CpuSet()
20 | cs.Id = uint32(index + i)
21 | cs.LogicalProcessorIndex = byte(i)
22 | if i%2 != 0 {
23 | cs.CoreIndex = lastCoreIndex
24 | } else {
25 | cs.CoreIndex = byte(i)
26 | lastCoreIndex = byte(i)
27 | }
28 | if i > 11 {
29 | cs.LastLevelCacheIndex = 12
30 | }
31 | }
32 | SystemCpuSets = cpuSet[:count]
33 | }
34 |
35 | // Intel i9 13900
36 | func Fake13900() {
37 | log.Println("Intel i9 13900")
38 | size := 0x20 * 64
39 | cpuSet := make([]SYSTEM_CPU_SET_INFORMATION, size)
40 | cpuSet[0].Size = 32
41 | var lastCoreIndex byte
42 | var count = 1024
43 | var index = 0x100
44 | for i := 0; i < count; i++ {
45 | cs := cpuSet[i].CpuSet()
46 | cs.Id = uint32(index + i)
47 | cs.LogicalProcessorIndex = byte(i)
48 | if i < 16 && i%2 != 0 {
49 | cs.CoreIndex = lastCoreIndex
50 | } else {
51 | cs.CoreIndex = byte(i)
52 | lastCoreIndex = byte(i)
53 | }
54 | if i < 16 {
55 | cs.EfficiencyClass = 1
56 | }
57 | }
58 | SystemCpuSets = cpuSet[:count]
59 | }
60 |
61 | // Intel i9 13900 Without HT
62 | func Fake13900WithoutHT() {
63 | log.Println("Intel i9 13900 Fake13900WithoutHT")
64 | size := 0x20 * 64
65 | cpuSet := make([]SYSTEM_CPU_SET_INFORMATION, size)
66 | cpuSet[0].Size = 32
67 | var count = 768
68 | var index = 0x100
69 | for i := 0; i < count; i++ {
70 | cs := cpuSet[i].CpuSet()
71 | cs.Id = uint32(index + i)
72 | cs.LogicalProcessorIndex = byte(i)
73 | cs.CoreIndex = byte(i)
74 | if i < 8 {
75 | cs.EfficiencyClass = 1
76 | }
77 | }
78 | SystemCpuSets = cpuSet[:count]
79 | }
80 |
81 | func Fake8Threads() {
82 | log.Println("Fake8Threads")
83 | size := 0x20 * 64
84 | cpuSet := make([]SYSTEM_CPU_SET_INFORMATION, size)
85 | cpuSet[0].Size = 32
86 | var count = 256
87 | var index = 0x100
88 | for i := 0; i < count; i++ {
89 | cs := cpuSet[i].CpuSet()
90 | cs.Id = uint32(index + i)
91 | cs.LogicalProcessorIndex = byte(i)
92 | cs.CoreIndex = byte(i)
93 | }
94 | SystemCpuSets = cpuSet[:count]
95 | }
96 |
97 | // ?
98 | func FakeNumaCCD12Core() {
99 | log.Println("FakeNumaCCD12Core")
100 | size := 0x20 * 64
101 | cpuSet := make([]SYSTEM_CPU_SET_INFORMATION, size)
102 | cpuSet[0].Size = 32
103 | var count = 384
104 | var index = 0x100
105 | for i := 0; i < count; i++ {
106 | cs := cpuSet[i].CpuSet()
107 | cs.Id = uint32(index + i)
108 | cs.LogicalProcessorIndex = byte(i)
109 | cs.CoreIndex = byte(i)
110 |
111 | if i > 5 {
112 | cs.LastLevelCacheIndex = 6
113 | cs.NumaNodeIndex = 6
114 | }
115 | }
116 | SystemCpuSets = cpuSet[:count]
117 | }
118 |
119 | // ?
120 | func Fake2CCD12CoreHT() {
121 | log.Println("Fake2CCD12CoreHT")
122 | size := 0x20 * 64
123 | cpuSet := make([]SYSTEM_CPU_SET_INFORMATION, size)
124 | cpuSet[0].Size = 32
125 | var lastCoreIndex byte
126 | var count = 768
127 | var index = 0x100
128 | for i := 0; i < count; i++ {
129 | cs := cpuSet[i].CpuSet()
130 | cs.Id = uint32(index + i)
131 | cs.LogicalProcessorIndex = byte(i)
132 | if i%2 != 0 {
133 | cs.CoreIndex = lastCoreIndex
134 | } else {
135 | cs.CoreIndex = byte(i)
136 | lastCoreIndex = byte(i)
137 | }
138 |
139 | if i > 11 {
140 | cs.LastLevelCacheIndex = 12
141 | }
142 | }
143 | SystemCpuSets = cpuSet[:count]
144 | }
145 |
146 | // Intel Core i5-13600KF
147 | func Fake13600KF() {
148 | log.Println("Intel i5 13600KF")
149 | size := 0x20 * 64
150 | cpuSet := make([]SYSTEM_CPU_SET_INFORMATION, size)
151 | cpuSet[0].Size = 32
152 | var lastCoreIndex byte
153 | var count = 640
154 | var index = 0x100
155 | for i := 0; i < count; i++ {
156 | cs := cpuSet[i].CpuSet()
157 | cs.Id = uint32(index + i)
158 | cs.LogicalProcessorIndex = byte(i)
159 | if i < 12 && i%2 != 0 {
160 | cs.CoreIndex = lastCoreIndex
161 | } else {
162 | cs.CoreIndex = byte(i)
163 | lastCoreIndex = byte(i)
164 | }
165 | if i < 12 {
166 | cs.EfficiencyClass = 1
167 | }
168 | }
169 | SystemCpuSets = cpuSet[:count]
170 | }
171 |
--------------------------------------------------------------------------------
/dialog.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "math"
7 | "os"
8 | "os/exec"
9 | "sort"
10 | "strings"
11 |
12 | "github.com/tailscale/walk"
13 | "golang.org/x/sys/windows/registry"
14 |
15 | //lint:ignore ST1001 standard behavior tailscale/walk
16 | . "github.com/tailscale/walk/declarative"
17 | )
18 |
19 | type IrqPolicys struct {
20 | Enums float64
21 | Name string
22 | }
23 |
24 | type IrqPrioritys struct {
25 | Enums float64
26 | Name string
27 | }
28 |
29 | type CheckBoxList struct {
30 | Widget []Widget
31 | List []*walk.CheckBox
32 | }
33 |
34 | func IrqPolicy() []*IrqPolicys {
35 | return []*IrqPolicys{
36 | {IrqPolicyMachineDefault, "IrqPolicyMachineDefault"},
37 | {IrqPolicyAllCloseProcessors, "IrqPolicyAllCloseProcessors"},
38 | {IrqPolicyOneCloseProcessor, "IrqPolicyOneCloseProcessor"},
39 | {IrqPolicyAllProcessorsInMachine, "IrqPolicyAllProcessorsInMachine"},
40 | {IrqPolicySpecifiedProcessors, "IrqPolicySpecifiedProcessors"},
41 | {IrqPolicySpreadMessagesAcrossAllProcessors, "IrqPolicySpreadMessagesAcrossAllProcessors"},
42 | }
43 | }
44 |
45 | func IrqPriority() []*IrqPrioritys {
46 | return []*IrqPrioritys{
47 | {0, "Undefined"},
48 | {1, "Low"},
49 | {2, "Normal"},
50 | {3, "High"},
51 | }
52 | }
53 |
54 | func RunDialog(owner walk.Form, device *Device) (int, error) {
55 | var dlg *walk.Dialog
56 | var db *walk.DataBinder
57 | var acceptPB, cancelPB *walk.PushButton
58 |
59 | var cpuArrayComView *walk.Composite
60 |
61 | var devicePolicyCB, devicePriorityCB *walk.ComboBox
62 | var deviceMessageNumberLimitNE *walk.NumberEdit
63 | var checkBoxList = new(CheckBoxList)
64 |
65 | return Dialog{
66 | AssignTo: &dlg,
67 | Title: Bind("'Device Policy' + (device.DeviceDesc == '' ? '' : ' - ' + device.DeviceDesc)"),
68 | DefaultButton: &acceptPB,
69 | CancelButton: &cancelPB,
70 | FixedSize: true,
71 | DataBinder: DataBinder{
72 | AssignTo: &db,
73 | Name: "device",
74 | DataSource: device,
75 | ErrorPresenter: ToolTipErrorPresenter{},
76 | },
77 | Layout: VBox{
78 | MarginsZero: true,
79 | },
80 | Children: []Widget{
81 | Composite{
82 | Layout: VBox{},
83 | Children: []Widget{
84 | Composite{
85 | Layout: Grid{
86 | Columns: 2,
87 | },
88 | Children: []Widget{
89 | Label{
90 | Text: "Name:",
91 | },
92 | Label{
93 | Text: Bind("device.DeviceDesc == '' ? 'N/A' : device.DeviceDesc"),
94 | },
95 |
96 | Label{
97 | Text: "Location Info:",
98 | },
99 | Label{
100 | Text: Bind("device.LocationInformation == '' ? 'N/A' : device.LocationInformation"),
101 | },
102 |
103 | Label{
104 | Text: "DevObj Name:",
105 | },
106 | LineEdit{
107 | Text: Bind("device.DevObjName == '' ? 'N/A' : device.DevObjName"),
108 | ReadOnly: true,
109 | },
110 | },
111 | },
112 |
113 | GroupBox{
114 | Title: "Message Signaled-Based Interrupts",
115 | Visible: device.MsiSupported != 2,
116 | Layout: Grid{Columns: 1},
117 | Children: []Widget{
118 |
119 | CheckBox{
120 | Name: "MsiSupported",
121 | Text: "MSI Mode:",
122 | TextOnLeftSide: true,
123 | Checked: device.MsiSupported == 1,
124 | // Checked: Bind("eq(device.MsiSupported, 1, 'MsiSupported')"), // dont work, idk
125 | OnClicked: func() {
126 | if device.MsiSupported == 0 {
127 | device.MsiSupported = 1
128 | deviceMessageNumberLimitNE.SetEnabled(true)
129 | device.MessageNumberLimit = uint32(deviceMessageNumberLimitNE.Value())
130 | } else {
131 | device.MsiSupported = 0
132 | deviceMessageNumberLimitNE.SetEnabled(false)
133 | }
134 | },
135 | },
136 |
137 | Composite{
138 | Layout: Grid{
139 | Columns: 3,
140 | MarginsZero: true,
141 | },
142 | Children: []Widget{
143 | LinkLabel{
144 | Text: `MSI Limit: ?`,
145 | OnLinkActivated: func(link *walk.LinkLabelLink) {
146 | // https://stackoverflow.com/a/12076082
147 | exec.Command("rundll32.exe", "url.dll,FileProtocolHandler", link.URL()).Start()
148 | },
149 | },
150 | NumberEdit{
151 | SpinButtonsVisible: true,
152 | AssignTo: &deviceMessageNumberLimitNE,
153 | Enabled: device.MsiSupported == 1,
154 | MinValue: 0,
155 | MaxValue: hasMsiX(device.InterruptTypeMap),
156 | Value: Bind("device.MessageNumberLimit < 1.0 ? 1.0 : device.MessageNumberLimit"),
157 | OnValueChanged: func() {
158 | device.MessageNumberLimit = uint32(deviceMessageNumberLimitNE.Value())
159 | },
160 | },
161 | },
162 | },
163 |
164 | Label{
165 | Text: "Interrupt Type: " + interruptType(device.InterruptTypeMap),
166 | },
167 |
168 | Label{
169 | Text: Bind("device.MaxMSILimit == 0 ? '' : 'Max MSI Limit: ' + device.MaxMSILimit"),
170 | },
171 | },
172 | },
173 |
174 | GroupBox{
175 | Title: "Advanced Policies",
176 | Layout: VBox{},
177 | Children: []Widget{
178 | Composite{
179 | Layout: Grid{
180 | Columns: 2,
181 | MarginsZero: true,
182 | },
183 | Children: []Widget{
184 | LinkLabel{
185 | Text: `Device Priority: ?`,
186 | OnLinkActivated: func(link *walk.LinkLabelLink) {
187 | // https://stackoverflow.com/a/12076082
188 | exec.Command("rundll32.exe", "url.dll,FileProtocolHandler", link.URL()).Start()
189 | },
190 | },
191 | ComboBox{
192 | AssignTo: &devicePriorityCB,
193 | Value: Bind("device.DevicePriority"),
194 | BindingMember: "Enums",
195 | DisplayMember: "Name",
196 | Model: IrqPriority(),
197 | OnCurrentIndexChanged: func() {
198 | device.DevicePriority = uint32(devicePriorityCB.CurrentIndex())
199 | },
200 | },
201 |
202 | LinkLabel{
203 | Text: `Device Policy: ?`,
204 | OnLinkActivated: func(link *walk.LinkLabelLink) {
205 | // https://stackoverflow.com/a/12076082
206 | exec.Command("rundll32.exe", "url.dll,FileProtocolHandler", link.URL()).Start()
207 | },
208 | },
209 | ComboBox{
210 | AssignTo: &devicePolicyCB,
211 | Value: Bind("device.DevicePolicy"),
212 | BindingMember: "Enums",
213 | DisplayMember: "Name",
214 | Model: IrqPolicy(),
215 | OnCurrentIndexChanged: func() {
216 | currentIndex := uint32(devicePolicyCB.CurrentIndex())
217 | if device.DevicePolicy == currentIndex {
218 | return
219 | }
220 |
221 | device.DevicePolicy = currentIndex
222 | if device.DevicePolicy == 4 {
223 | cpuArrayComView.SetVisible(true)
224 | return
225 | }
226 |
227 | cpuArrayComView.SetVisible(false)
228 |
229 | if err := dlg.SetSize(walk.Size{Width: 0, Height: 0}); err != nil {
230 | panic(err)
231 | }
232 |
233 | },
234 | },
235 | },
236 | },
237 |
238 | Composite{
239 | AssignTo: &cpuArrayComView,
240 | Layout: VBox{MarginsZero: true},
241 | Visible: Bind("device.DevicePolicy == 4"),
242 | Children: []Widget{
243 | Composite{
244 | Alignment: AlignHNearVNear,
245 | Layout: HBox{
246 | Alignment: Alignment2D(walk.AlignHNearVNear),
247 | MarginsZero: true,
248 | },
249 | Children: checkBoxList.create(&device.AssignmentSetOverride),
250 | },
251 | GroupBox{
252 | Title: "Presets for Specified Processors:",
253 | Layout: HBox{},
254 | Children: []Widget{
255 | PushButton{
256 | Text: "All On",
257 | OnClicked: func() {
258 | checkBoxList.allOn(&device.AssignmentSetOverride)
259 | },
260 | },
261 |
262 | PushButton{
263 | Text: "All Off",
264 | OnClicked: func() {
265 | checkBoxList.allOff(&device.AssignmentSetOverride)
266 | },
267 | },
268 |
269 | PushButton{
270 | Text: "HT Off",
271 | Visible: cs.HyperThreading,
272 | OnClicked: func() {
273 | checkBoxList.htOff(&device.AssignmentSetOverride)
274 | },
275 | },
276 |
277 | PushButton{
278 | Text: "P-Core Only",
279 | Visible: cs.EfficiencyClass,
280 | OnClicked: func() {
281 | checkBoxList.pCoreOnly(&device.AssignmentSetOverride)
282 | },
283 | },
284 |
285 | PushButton{
286 | Text: "E-Core Only",
287 | Visible: cs.EfficiencyClass,
288 | OnClicked: func() {
289 | checkBoxList.eCoreOnly(&device.AssignmentSetOverride)
290 | },
291 | },
292 |
293 | HSpacer{},
294 | },
295 | },
296 | },
297 | },
298 | },
299 | },
300 |
301 | GroupBox{
302 | Title: "Registry",
303 | Layout: HBox{},
304 | Children: []Widget{
305 | PushButton{
306 | Text: "Open Device",
307 | OnClicked: func() {
308 | regPath, err := GetRegistryLocation(uintptr(device.reg))
309 | if err != nil {
310 | walk.MsgBox(dlg, "NtQueryKey Error", err.Error(), walk.MsgBoxOK)
311 | }
312 |
313 | k, _, err := registry.CreateKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Applets\Regedit`, registry.SET_VALUE)
314 | if err != nil {
315 | walk.MsgBox(dlg, "Registry Error", err.Error(), walk.MsgBoxOK)
316 | log.Fatal(err)
317 | }
318 | defer k.Close()
319 |
320 | if err := k.SetStringValue("LastKey", regPath); err == nil {
321 | exec.Command("regedit", "-m").Start()
322 | }
323 | },
324 | },
325 | PushButton{
326 | Text: "Export current settings",
327 | OnClicked: func() {
328 | regPath, err := GetRegistryLocation(uintptr(device.reg))
329 | if err != nil {
330 | walk.MsgBox(dlg, "NtQueryKey Error", err.Error(), walk.MsgBoxOK)
331 | }
332 |
333 | path, err := os.Getwd()
334 | if err != nil {
335 | log.Println(err)
336 | }
337 |
338 | filePath, cancel, err := saveFileExplorer(dlg, path, strings.ReplaceAll(device.DeviceDesc, " ", "_")+".reg", "Save current settings", "Registry File (*.reg)|*.reg")
339 | if !cancel || err != nil {
340 | file, err := os.Create(filePath)
341 | if err != nil {
342 | return
343 | }
344 | defer file.Close()
345 |
346 | file.WriteString(createRegFile(regPath, device))
347 | }
348 | },
349 | },
350 | HSpacer{},
351 | },
352 | },
353 | },
354 | Functions: map[string]func(args ...interface{}) (interface{}, error){
355 | "checkIrqPolicy": func(args ...interface{}) (interface{}, error) {
356 | for _, v := range IrqPolicy() {
357 | if v.Enums == args[0].(float64) {
358 | return v.Name, nil
359 | }
360 | }
361 | return "", nil
362 | },
363 | "viewAsHex": func(args ...interface{}) (interface{}, error) {
364 | if args[0].(Bits) == ZeroBit {
365 | return "N/A", nil
366 | }
367 | bits := args[0].(Bits)
368 | var result []string
369 | for bit, cpu := range CPUMap {
370 | if Has(bit, bits) {
371 | result = append(result, cpu)
372 | }
373 | }
374 | return strings.Join(result, ", "), nil
375 | },
376 | "eq": func(args ...interface{}) (interface{}, error) {
377 | if len(args) != 2 {
378 | return false, nil
379 | }
380 | switch v := args[0].(type) {
381 | case float64:
382 | if v == args[1].(float64) {
383 | return true, nil
384 | }
385 | case Bits:
386 | if v == Bits(args[1].(float64)) {
387 | return true, nil
388 | }
389 | default:
390 | log.Printf("I don't know about type %T!\n", v)
391 | }
392 |
393 | return false, nil
394 | },
395 | },
396 | },
397 | VSpacer{},
398 | Composite{
399 | Layout: HBox{},
400 | Children: []Widget{
401 | HSpacer{},
402 | PushButton{
403 | AssignTo: &acceptPB,
404 | Text: "OK",
405 | OnClicked: func() {
406 | if device.DevicePolicy == 4 && device.AssignmentSetOverride == ZeroBit {
407 | walk.MsgBox(dlg, "Invalid Option", "The affinity mask must contain at least one processor.", walk.MsgBoxIconError)
408 | } else {
409 | if err := db.Submit(); err != nil {
410 | return
411 | }
412 | dlg.Accept()
413 | }
414 | },
415 | },
416 | PushButton{
417 | AssignTo: &cancelPB,
418 | Text: "Cancel",
419 | OnClicked: func() { dlg.Cancel() },
420 | },
421 | },
422 | },
423 | },
424 | }.Run(owner)
425 | }
426 |
427 | func (checkboxlist *CheckBoxList) create(bits *Bits) []Widget {
428 | var children, partThread, partCore, partNUMA, partGroup, partCache []Widget
429 | var lastEfficiencyClass, lastNumaNodeIndex, lastLastLevelCache byte
430 | var cpuCount, numaCount, llcCount int
431 |
432 | checkboxlist.List = make([]*walk.CheckBox, len(cs.CPU))
433 | for i, cpuThread := range cs.CPU {
434 | // fmt.Printf("\n\n%d - Core: %d, LogicalProc: %d, Effi: %d\n", i,cpu.CoreIndex, cs.CPU[i].LogicalProcessorIndex, cs.CPU[i].EfficiencyClass)
435 |
436 | var isLastItem = i+1 == cs.CoreCount
437 | if i == 0 { // The EfficiencyClass starts with 1 on the Intel Gen12+
438 | lastEfficiencyClass = cpuThread.EfficiencyClass
439 | }
440 |
441 | if len(partThread) != 0 && cpuThread.CoreIndex == cpuThread.LogicalProcessorIndex {
442 | partCore = append(partCore, GroupBox{
443 | Title: fmt.Sprintf("Core %d", cpuCount),
444 | Alignment: AlignHCenterVNear,
445 | Layout: VBox{
446 | Margins: CalculateMargins(len(partThread)),
447 | },
448 | Children: partThread,
449 | })
450 | cpuCount++
451 | partThread = nil
452 | }
453 |
454 | local_CPUBits := CPUBits[i]
455 | checkboxlist.List[i] = new(walk.CheckBox)
456 |
457 | partThread = append(partThread, CheckBox{
458 | ColumnSpan: 3,
459 | RowSpan: 3,
460 | AlwaysConsumeSpace: true,
461 | StretchFactor: 2,
462 | Text: fmt.Sprintf("Thread %d", cpuThread.LogicalProcessorIndex),
463 | AssignTo: &checkboxlist.List[i],
464 | Checked: Has(*bits, local_CPUBits),
465 | OnClicked: func() {
466 | *bits = Toggle(local_CPUBits, *bits)
467 | },
468 | })
469 |
470 | if isLastItem {
471 | partCore = append(partCore, GroupBox{
472 | Title: fmt.Sprintf("Core %d", cpuCount),
473 | Alignment: AlignHCenterVNear,
474 | Layout: VBox{
475 | Margins: CalculateMargins(len(partThread)),
476 | },
477 | Children: partThread,
478 | })
479 | }
480 |
481 | if i == 0 {
482 | continue
483 | }
484 |
485 | if cs.EfficiencyClass && (lastEfficiencyClass != cpuThread.EfficiencyClass || isLastItem) {
486 | var title string
487 | // https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-system_cpu_set_information
488 |
489 | if isIntel() && cs.EfficiencyClass {
490 | if lastEfficiencyClass == 0 {
491 | title = "E-Cores"
492 | } else {
493 | title = "P-Cores"
494 | }
495 | } else { // AMD
496 | title = fmt.Sprintf("EfficiencyClass %d", lastEfficiencyClass)
497 | }
498 |
499 | partNUMA = append(partNUMA, GroupBox{
500 | Title: title,
501 | ToolTipText: ToolTipTextEfficiencyClass,
502 | Layout: Grid{
503 | Columns: mathCeilInInt(len(partCore), cs.Layout[lastEfficiencyClass].Rows),
504 | },
505 | Children: partCore,
506 | })
507 |
508 | partCore = nil
509 | cpuCount = 0
510 | }
511 |
512 | if cs.LastLevelCache && (lastLastLevelCache != cpuThread.LastLevelCacheIndex || isLastItem) {
513 | var title string
514 | if isAMD() {
515 | title = fmt.Sprintf("CCD %d", llcCount)
516 | } else {
517 | title = fmt.Sprintf("LLC %d", llcCount)
518 | }
519 |
520 | switch {
521 | case len(partNUMA) != 0:
522 | partGroup = append(partGroup, GroupBox{
523 | Title: title,
524 | ToolTipText: ToolTipTextLastLevelCache,
525 | Layout: Grid{
526 | Columns: len(partNUMA) / 2,
527 | },
528 | Children: partNUMA,
529 | })
530 | partNUMA = nil
531 | case len(partCore) != 0:
532 | partGroup = append(partGroup, GroupBox{
533 | Title: title,
534 | ToolTipText: ToolTipTextLastLevelCache,
535 | Layout: Grid{
536 | Columns: mathCeilInInt(len(partCore), cs.Layout[lastEfficiencyClass].Rows),
537 | },
538 | Children: partCore,
539 | })
540 | llcCount++
541 | partCore = nil
542 | cpuCount = 0
543 | }
544 | }
545 |
546 | if cs.NumaNode && (lastNumaNodeIndex != cpuThread.NumaNodeIndex || isLastItem) {
547 | switch {
548 | case len(partGroup) != 0:
549 | partCache = append(partCache, GroupBox{
550 | Title: fmt.Sprintf("NUMA %d", numaCount),
551 | ToolTipText: ToolTipTextNumaNode,
552 | Layout: Grid{
553 | Columns: len(partGroup) / 2,
554 | },
555 | Children: partGroup,
556 | })
557 | partGroup = nil
558 | case len(partNUMA) != 0:
559 | partGroup = append(partGroup, GroupBox{
560 | Title: fmt.Sprintf("NUMA %d", numaCount),
561 | ToolTipText: ToolTipTextNumaNode,
562 | Layout: Grid{
563 | Columns: len(partNUMA) / 2,
564 | },
565 | Children: partNUMA,
566 | })
567 | partNUMA = nil
568 | case len(partCore) != 0:
569 | partGroup = append(partGroup, GroupBox{
570 | Title: fmt.Sprintf("NUMA %d", numaCount),
571 | ToolTipText: ToolTipTextNumaNode,
572 | Layout: Grid{
573 | Columns: cs.Layout[lastEfficiencyClass].Cols,
574 | },
575 | Children: partCore,
576 | })
577 | partCore = nil
578 | cpuCount = 0
579 | }
580 | numaCount++
581 | }
582 |
583 | lastEfficiencyClass = cpuThread.EfficiencyClass
584 | lastNumaNodeIndex = cpuThread.NumaNodeIndex
585 | lastLastLevelCache = cpuThread.LastLevelCacheIndex
586 | }
587 |
588 | switch {
589 | case len(partCache) > 0:
590 | return partCache
591 | case len(partGroup) > 0:
592 | return partGroup
593 | case len(partNUMA) > 0:
594 | return partNUMA
595 | case len(partCore) > 0:
596 | return []Widget{
597 | Composite{
598 | Layout: Grid{
599 | Columns: cs.Layout[lastEfficiencyClass].Cols,
600 | },
601 | Children: partCore,
602 | },
603 | }
604 | default:
605 | return children
606 | }
607 | }
608 |
609 | func (checkboxlist *CheckBoxList) allOn(bits *Bits) {
610 | for i := 0; i < len(checkboxlist.List); i++ {
611 | *bits = Set(CPUBits[i], *bits)
612 | checkboxlist.List[i].SetChecked(true)
613 | }
614 | }
615 | func (checkboxlist *CheckBoxList) allOff(bits *Bits) {
616 | for i := 0; i < len(checkboxlist.List); i++ {
617 | checkboxlist.List[i].SetChecked(false)
618 | }
619 | *bits = Bits(0)
620 | }
621 |
622 | func (checkboxlist *CheckBoxList) htOff(bits *Bits) {
623 | for i := 0; i < len(cs.CPU); i++ {
624 | if cs.CPU[i].CoreIndex != cs.CPU[i].LogicalProcessorIndex {
625 | checkboxlist.List[i].SetChecked(false)
626 | if Has(CPUBits[i], *bits) {
627 | *bits = Toggle(CPUBits[i], *bits)
628 | }
629 | }
630 | }
631 | }
632 |
633 | func (checkboxlist *CheckBoxList) pCoreOnly(bits *Bits) {
634 | for i := 0; i < len(cs.CPU); i++ {
635 | if cs.CPU[i].EfficiencyClass == 1 {
636 | checkboxlist.List[i].SetChecked(true)
637 | *bits = Set(CPUBits[i], *bits)
638 | } else {
639 | checkboxlist.List[i].SetChecked(false)
640 | if Has(CPUBits[i], *bits) {
641 | *bits = Toggle(CPUBits[i], *bits)
642 | }
643 | }
644 | }
645 | }
646 | func (checkboxlist *CheckBoxList) eCoreOnly(bits *Bits) {
647 | for i := 0; i < len(cs.CPU); i++ {
648 | if cs.CPU[i].EfficiencyClass == 0 {
649 | checkboxlist.List[i].SetChecked(true)
650 | *bits = Set(CPUBits[i], *bits)
651 | } else {
652 | checkboxlist.List[i].SetChecked(false)
653 | if Has(CPUBits[i], *bits) {
654 | *bits = Toggle(CPUBits[i], *bits)
655 | }
656 | }
657 | }
658 | }
659 |
660 | // https://docs.microsoft.com/de-de/windows-hardware/drivers/kernel/enabling-message-signaled-interrupts-in-the-registry
661 | func hasMsiX(b Bits) float64 {
662 | if Has(b, Bits(4)) {
663 | return 2048 // MSIX
664 | } else {
665 | return 16 // MSI
666 | }
667 | }
668 |
669 | func interruptType(b Bits) string {
670 | if b == ZeroBit {
671 | return ""
672 | }
673 | var types []string
674 | for bit, name := range InterruptTypeMap {
675 | if Has(b, bit) {
676 | types = append(types, name)
677 | }
678 | }
679 | sort.Strings(types)
680 | return strings.Join(types, ", ")
681 | }
682 |
683 | func CalculateMargins(value int) Margins {
684 | if cs.MaxThreadsPerCore+1 == value {
685 | return Margins{
686 | Left: 9,
687 | Top: 9,
688 | Right: 9,
689 | Bottom: 9,
690 | }
691 | } else {
692 | part := (11.75 * float64(cs.MaxThreadsPerCore+1) / float64(value))
693 | return Margins{
694 | Left: 9,
695 | Top: int(math.Floor(part)),
696 | Right: 9,
697 | Bottom: int(math.Ceil(part)),
698 | }
699 | }
700 | }
701 |
--------------------------------------------------------------------------------
/flags.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "os"
7 | )
8 |
9 | var (
10 | flagDevObjName string
11 | flagDevicePriority int
12 | flagDevicePolicy int
13 | flagMsiSupported int
14 | flagMessageNumberLimit int
15 | flagCPU string
16 | flagRestart bool
17 | flagRestartOnChange bool
18 | flagHelp bool
19 |
20 | CLIMode bool
21 | )
22 |
23 | func init() {
24 | flag.StringVar(&flagDevObjName, "devobj", "", "\\Device\\00000123")
25 | flag.StringVar(&flagCPU, "cpu", "", "e.g. 0,1,2,4")
26 | flag.IntVar(&flagDevicePriority, "priority", -1, "0=Undefined, 1=Low, 2=Normal, 3=High")
27 | flag.IntVar(&flagDevicePolicy, "policy", -1, "0=Default, 1=All Close Proc, 2=One Close Proc, 3=All Proc in Machine, 4=Specified Proc, 5=Spread Messages Across All Proc")
28 | flag.IntVar(&flagMsiSupported, "msisupported", -1, "0=Off, 1=On")
29 | flag.IntVar(&flagMessageNumberLimit, "msilimit", -1, "Message Signaled Interrupt Limit")
30 | flag.BoolVar(&flagRestart, "restart", false, "Restart target device")
31 | flag.BoolVar(&flagRestartOnChange, "restart-on-change", false, "Restart target device on change")
32 | flag.BoolVar(&flagHelp, "help", false, "Print Defaults")
33 |
34 | flag.Parse()
35 | if flagHelp {
36 | fmt.Printf("Usage: %s [OPTIONS] argument ...\n", os.Args[0])
37 | flag.PrintDefaults()
38 | os.Exit(0)
39 | }
40 |
41 | if flagDevObjName != "" || flagDevicePriority != -1 || flagDevicePolicy != -1 || flagMsiSupported != -1 || flagMessageNumberLimit != -1 || flagRestart || flagRestartOnChange {
42 | CLIMode = true
43 | }
44 |
45 | if flagDevObjName != "" {
46 | fmt.Println("DevObjName:", flagDevObjName)
47 | }
48 | if flagDevicePriority != -1 {
49 | var prio string
50 | switch flagDevicePriority {
51 | case 0:
52 | prio = "Undefined"
53 | case 1:
54 | prio = "Low"
55 | case 2:
56 | prio = "Normal"
57 | case 3:
58 | prio = "High"
59 | default:
60 | prio = fmt.Sprintf("%d", flagDevicePriority)
61 | }
62 | fmt.Println("DevicePriority:", prio)
63 | }
64 | if flagDevicePolicy != -1 {
65 | var policy string
66 | switch flagDevicePolicy {
67 | case IrqPolicyMachineDefault: // 0x00
68 | policy = "Default"
69 | case IrqPolicyAllCloseProcessors: // 0x01
70 | policy = "All Close Proc"
71 | case IrqPolicyOneCloseProcessor: // 0x02
72 | policy = "One Close Proc"
73 | case IrqPolicyAllProcessorsInMachine: // 0x03
74 | policy = "All Proc in Machine"
75 | case IrqPolicySpecifiedProcessors: // 0x04
76 | policy = "Specified Proc"
77 | case IrqPolicySpreadMessagesAcrossAllProcessors: // 0x05
78 | policy = "Spread Messages Across All Proc"
79 | default:
80 | policy = fmt.Sprintf("%d", flagDevicePolicy)
81 | }
82 | fmt.Println("DevicePolicy:", policy)
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/spddl/GoInterruptPolicy
2 |
3 | go 1.22.4
4 |
5 | require (
6 | github.com/intel-go/cpuid v0.0.0-20220614022739-219e067757cb
7 | github.com/tailscale/walk v0.0.0-20240108184108-6a278000867c
8 | golang.org/x/sys v0.21.0
9 | )
10 |
11 | require (
12 | github.com/dblohm7/wingoes v0.0.0-20240123200102-b75a8a7d7eb0 // indirect
13 |
14 | github.com/tailscale/win v0.0.0-20240403170916-6580b55d49ca // indirect
15 | golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect
16 | gopkg.in/Knetic/govaluate.v3 v3.0.0 // indirect
17 | )
18 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/dblohm7/wingoes v0.0.0-20231025182615-65d8b4b5428f h1:c5mkOIXbHZVKGQaSEZZyLW9ORD+h4PT2TPF8IQPwyOs=
2 | github.com/dblohm7/wingoes v0.0.0-20231025182615-65d8b4b5428f/go.mod h1:6NCrWM5jRefaG7iN0iMShPalLsljHWBh9v1zxM2f8Xs=
3 | github.com/dblohm7/wingoes v0.0.0-20240123200102-b75a8a7d7eb0 h1:vrC07UZcgPzu/OjWsmQKMGg3LoPSz9jh/pQXIrHjUj4=
4 | github.com/dblohm7/wingoes v0.0.0-20240123200102-b75a8a7d7eb0/go.mod h1:Nx87SkVqTKd8UtT+xu7sM/l+LgXs6c0aHrlKusR+2EQ=
5 | github.com/intel-go/cpuid v0.0.0-20220614022739-219e067757cb h1:Fg0Y/RDZ6UPwl3o7/IzPbneDq8g9+gH6DPs42KFUsy8=
6 | github.com/intel-go/cpuid v0.0.0-20220614022739-219e067757cb/go.mod h1:RmeVYf9XrPRbRc3XIx0gLYA8qOFvNoPOfaEZduRlEp4=
7 | github.com/tailscale/walk v0.0.0-20240108184108-6a278000867c h1:ae253wBb81mm12KjLAgclX3yoSqlNIJGPv75RDCUSNY=
8 | github.com/tailscale/walk v0.0.0-20240108184108-6a278000867c/go.mod h1:Gfr0f015WTTUOroY5UICn/mlyIZdTa2moSmPpAub9Ys=
9 | github.com/tailscale/win v0.0.0-20230710211752-84569fd814a9 h1:K3/RR+xb+WWRC19RSctFc+81gwA4+vlz+I9qME9asHg=
10 | github.com/tailscale/win v0.0.0-20230710211752-84569fd814a9/go.mod h1:bCmhgMXv5K6RcDeQFxOZWbZW18dKNLyihfZ5tzuJ0fk=
11 | github.com/tailscale/win v0.0.0-20240403170916-6580b55d49ca h1:oYYp7AvemDkWtxmsgVSpcFxcHMv/2jjbOk5ZC16e77Y=
12 | github.com/tailscale/win v0.0.0-20240403170916-6580b55d49ca/go.mod h1:aMd4yDHLjbOuYP6fMxj1d9ACDQlSWwYztcpybGHCQc8=
13 | golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
14 | golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
15 | golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM=
16 | golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
17 | golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
18 | golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
19 | golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
20 | golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
21 | gopkg.in/Knetic/govaluate.v3 v3.0.0 h1:18mUyIt4ZlRlFZAAfVetz4/rzlJs9yhN+U02F4u1AOc=
22 | gopkg.in/Knetic/govaluate.v3 v3.0.0/go.mod h1:csKLBORsPbafmSCGTEh3U7Ozmsuq8ZSIlKk1bcqph0E=
23 |
--------------------------------------------------------------------------------
/guid.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "syscall"
6 | "unsafe"
7 |
8 | "golang.org/x/sys/windows"
9 | )
10 |
11 | var _ unsafe.Pointer
12 |
13 | var (
14 | modole32 = windows.NewLazySystemDLL("ole32.dll")
15 |
16 | procCLSIDFromString = modole32.NewProc("CLSIDFromString")
17 | )
18 |
19 | func clsidFromString(lpsz *uint16, pclsid *windows.GUID) (hr int32) {
20 | r0, _, _ := syscall.SyscallN(procCLSIDFromString.Addr(), uintptr(unsafe.Pointer(lpsz)), uintptr(unsafe.Pointer(pclsid)))
21 | hr = int32(r0)
22 | return
23 | }
24 |
25 | // sys clsidFromString(lpsz *uint16, pclsid *windows.GUID) (hr int32) = ole32.CLSIDFromString
26 |
27 | // FromString parses "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}" string to GUID.
28 | func FromString(str string) (*windows.GUID, error) {
29 | strUTF16, err := syscall.UTF16PtrFromString(str)
30 | if err != nil {
31 | return nil, err
32 | }
33 |
34 | guid := &windows.GUID{}
35 |
36 | hr := clsidFromString(strUTF16, guid)
37 | if hr < 0 {
38 | return nil, syscall.Errno(hr)
39 | }
40 |
41 | return guid, nil
42 | }
43 |
44 | // ToString function converts GUID to string
45 | // "{XXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}".
46 | //
47 | // The resulting string is uppercase.
48 | func ToString(guid *windows.GUID) string {
49 | return fmt.Sprintf("{%06X-%04X-%04X-%04X-%012X}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[:2], guid.Data4[2:])
50 | }
51 |
--------------------------------------------------------------------------------
/init.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "strconv"
6 | "time"
7 |
8 | "golang.org/x/sys/windows/registry"
9 | )
10 |
11 | type Device struct {
12 | Idata DevInfoData
13 | reg registry.Key
14 | IrqPolicy int32
15 | DeviceDesc string
16 | DeviceIDs []string
17 | DevObjName string
18 | Driver string
19 | LocationInformation string
20 | FriendlyName string
21 | LastChange time.Time
22 |
23 | // AffinityPolicy
24 | DevicePolicy uint32
25 | DevicePriority uint32
26 | AssignmentSetOverride Bits
27 |
28 | // MessageSignaledInterruptProperties
29 | MsiSupported uint32
30 | MessageNumberLimit uint32
31 | MaxMSILimit uint32
32 | InterruptTypeMap Bits
33 | }
34 |
35 | const (
36 | // https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/interrupt-affinity-and-priority
37 | IrqPolicyMachineDefault = iota // 0
38 | IrqPolicyAllCloseProcessors // 1
39 | IrqPolicyOneCloseProcessor // 2
40 | IrqPolicyAllProcessorsInMachine // 3
41 | IrqPolicySpecifiedProcessors // 4
42 | IrqPolicySpreadMessagesAcrossAllProcessors // 5
43 | )
44 |
45 | type Bits uint64
46 |
47 | var CPUMap map[Bits]string
48 |
49 | var CPUBits []Bits
50 | var InterruptTypeMap = map[Bits]string{
51 | 0: "unknown",
52 | 1: "LineBased",
53 | 2: "Msi",
54 |
55 | 4: "MsiX",
56 | }
57 |
58 | var sysInfo SystemInfo
59 | var handle DevInfo
60 |
61 | const ZeroBit = Bits(0)
62 |
63 | func init() {
64 | log.SetFlags(log.LstdFlags | log.Lshortfile)
65 |
66 | sysInfo = GetSystemInfo()
67 | CPUMap = make(map[Bits]string, sysInfo.NumberOfProcessors)
68 | var index Bits = 1
69 | for i := 0; i < int(sysInfo.NumberOfProcessors); i++ {
70 | indexString := strconv.Itoa(i)
71 | CPUMap[index] = indexString
72 | CPUBits = append(CPUBits, index)
73 | index *= 2
74 | }
75 | }
76 |
77 | func Set(b, flag Bits) Bits { return b | flag }
78 | func Clear(b, flag Bits) Bits { return b &^ flag }
79 | func Toggle(b, flag Bits) Bits { return b ^ flag }
80 | func Has(b, flag Bits) bool { return b&flag != 0 }
81 |
82 | // https://gist.github.com/chiro-hiro/2674626cebbcb5a676355b7aaac4972d
83 | func i64tob(val uint64) []byte {
84 | r := make([]byte, 8)
85 | for i := uint64(0); i < 8; i++ {
86 | r[i] = byte((val >> (i * 8)) & 0xff)
87 | }
88 | return r
89 | }
90 |
91 | func btoi64(val []byte) uint64 {
92 | r := uint64(0)
93 | for i := uint64(0); i < 8; i++ {
94 | r |= uint64(val[i]) << (8 * i)
95 | }
96 | return r
97 | }
98 |
99 | func btoi32(val []byte) uint32 {
100 | r := uint32(0)
101 | for i := uint32(0); i < 4; i++ {
102 | r |= uint32(val[i]) << (8 * i)
103 | }
104 | return r
105 | }
106 |
107 | func btoi16(val []byte) uint16 {
108 | r := uint16(0)
109 | for i := uint16(0); i < 2; i++ {
110 | r |= uint16(val[i]) << (8 * i)
111 | }
112 | return r
113 | }
114 |
--------------------------------------------------------------------------------
/kernel32.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | // https://github.com/prometheus-community/windows_exporter/blob/74eac8f29b8083b9e6a4832d739748739e4e3fe0/headers/sysinfoapi/sysinfoapi.go#L44
4 |
5 | import (
6 | "syscall"
7 | "unsafe"
8 |
9 | "golang.org/x/sys/windows"
10 | )
11 |
12 | // wProcessorArchitecture is a wrapper for the union found in LP_SYSTEM_INFO
13 | // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
14 | type wProcessorArchitecture struct {
15 | WProcessorArchitecture uint16
16 | WReserved uint16
17 | }
18 |
19 | // ProcessorArchitecture is an idiomatic wrapper for wProcessorArchitecture
20 | type ProcessorArchitecture uint16
21 |
22 | // Idiomatic values for wProcessorArchitecture
23 | const (
24 | AMD64 ProcessorArchitecture = 9
25 | ARM ProcessorArchitecture = 5
26 | ARM64 ProcessorArchitecture = 12
27 | IA64 ProcessorArchitecture = 6
28 | INTEL ProcessorArchitecture = 0
29 | UNKNOWN ProcessorArchitecture = 0xffff
30 | )
31 |
32 | // SystemInfo is an idiomatic wrapper for LpSystemInfo
33 | type SystemInfo struct {
34 | Arch ProcessorArchitecture
35 | PageSize uint32
36 | MinimumApplicationAddress uintptr
37 | MaximumApplicationAddress uintptr
38 | ActiveProcessorMask uint
39 | NumberOfProcessors uint32
40 | ProcessorType uint32
41 | AllocationGranularity uint32
42 | ProcessorLevel uint16
43 | ProcessorRevision uint16
44 | }
45 |
46 | // LpSystemInfo is a wrapper for LPSYSTEM_INFO
47 | // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
48 | type lpSystemInfo struct {
49 | Arch wProcessorArchitecture
50 | DwPageSize uint32
51 | LpMinimumApplicationAddress uintptr
52 | LpMaximumApplicationAddress uintptr
53 | DwActiveProcessorMask uint
54 | DwNumberOfProcessors uint32
55 | DwProcessorType uint32
56 | DwAllocationGranularity uint32
57 | WProcessorLevel uint16
58 | WProcessorRevision uint16
59 | }
60 |
61 | var (
62 | // Library
63 | libKernel32 = windows.NewLazySystemDLL("kernel32.dll")
64 |
65 | // Functions
66 | getSystemCpuSetInformation = libKernel32.NewProc("GetSystemCpuSetInformation")
67 | getSystemInfo = libKernel32.NewProc("GetSystemInfo")
68 | )
69 |
70 | // GetSystemInfo is an idiomatic wrapper for the GetSystemInfo function from sysinfoapi
71 | // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsysteminfo
72 | func GetSystemInfo() SystemInfo {
73 | var info lpSystemInfo
74 | getSystemInfo.Call(uintptr(unsafe.Pointer(&info)))
75 | return SystemInfo{
76 | Arch: ProcessorArchitecture(info.Arch.WProcessorArchitecture),
77 | PageSize: info.DwPageSize,
78 | MinimumApplicationAddress: info.LpMinimumApplicationAddress,
79 | MaximumApplicationAddress: info.LpMinimumApplicationAddress,
80 | ActiveProcessorMask: info.DwActiveProcessorMask,
81 | NumberOfProcessors: info.DwNumberOfProcessors,
82 | ProcessorType: info.DwProcessorType,
83 | AllocationGranularity: info.DwAllocationGranularity,
84 | ProcessorLevel: info.WProcessorLevel,
85 | ProcessorRevision: info.WProcessorRevision,
86 | }
87 | }
88 |
89 | // The SystemInformationClass constants have been derived from the SYSTEM_INFORMATION_CLASS enum definition.
90 | const (
91 | SystemAllowedCpuSetsInformation = 0xA8
92 | SystemCpuSetInformation = 0xAF
93 |
94 | ProcessDefaultCpuSetsInformation = 0x42
95 |
96 | // https://learn.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights
97 | PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
98 | PROCESS_SET_LIMITED_INFORMATION = 0x2000
99 | )
100 |
101 | const (
102 | SYSTEM_CPU_SET_INFORMATION_PARKED uint32 = 0x1
103 | SYSTEM_CPU_SET_INFORMATION_ALLOCATED uint32 = 0x2
104 | SYSTEM_CPU_SET_INFORMATION_ALLOCATED_TO_TARGET_PROCESS uint32 = 0x4
105 | SYSTEM_CPU_SET_INFORMATION_REALTIME uint32 = 0x8
106 | )
107 |
108 | // https://github.com/zzl/go-win32api/blob/d9f481c2ab64b5df06e8d62e1bac33dd9141de43/win32/System.SystemInformation.go
109 |
110 | type SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous1_Anonymous struct {
111 | Bitfield_ byte
112 | }
113 |
114 | type SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous1 struct {
115 | SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous1_Anonymous
116 | }
117 |
118 | func (t *SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous1) AllFlags() *byte {
119 | return (*byte)(unsafe.Pointer(t))
120 | }
121 |
122 | func (t *SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous1) AllFlagsVal() byte {
123 | return *(*byte)(unsafe.Pointer(t))
124 | }
125 |
126 | func (t *SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous1) Anonymous() *SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous1_Anonymous {
127 | return (*SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous1_Anonymous)(unsafe.Pointer(t))
128 | }
129 |
130 | func (t *SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous1) AnonymousVal() SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous1_Anonymous {
131 | return *(*SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous1_Anonymous)(unsafe.Pointer(t))
132 | }
133 |
134 | type SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous2 struct {
135 | Data [1]uint32
136 | }
137 |
138 | func (t *SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous2) Reserved() *uint32 {
139 | return (*uint32)(unsafe.Pointer(t))
140 | }
141 |
142 | func (t *SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous2) ReservedVal() uint32 {
143 | return *(*uint32)(unsafe.Pointer(t))
144 | }
145 |
146 | func (t *SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous2) SchedulingClass() *byte {
147 | return (*byte)(unsafe.Pointer(t))
148 | }
149 |
150 | func (t *SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous2) SchedulingClassVal() byte {
151 | return *(*byte)(unsafe.Pointer(t))
152 | }
153 |
154 | type SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet struct {
155 | Id uint32
156 | Group uint16
157 | LogicalProcessorIndex byte
158 | CoreIndex byte
159 | LastLevelCacheIndex byte
160 | NumaNodeIndex byte
161 | EfficiencyClass byte
162 | SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous1
163 | SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet_Anonymous2
164 | AllocationTag uint64
165 | }
166 |
167 | type SYSTEM_CPU_SET_INFORMATION_Anonymous struct {
168 | Data [3]uint64
169 | }
170 |
171 | func (t *SYSTEM_CPU_SET_INFORMATION_Anonymous) CpuSet() *SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet {
172 | return (*SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet)(unsafe.Pointer(t))
173 | }
174 |
175 | func (t *SYSTEM_CPU_SET_INFORMATION_Anonymous) CpuSetVal() SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet {
176 | return *(*SYSTEM_CPU_SET_INFORMATION_Anonymous_CpuSet)(unsafe.Pointer(t))
177 | }
178 |
179 | type CPU_SET_INFORMATION_TYPE int32
180 |
181 | type SYSTEM_CPU_SET_INFORMATION struct {
182 | Size uint32
183 | Type CPU_SET_INFORMATION_TYPE
184 | SYSTEM_CPU_SET_INFORMATION_Anonymous
185 | }
186 |
187 | func GetSystemCpuSetInformation(
188 | information *SYSTEM_CPU_SET_INFORMATION,
189 | bufferLength uint32,
190 | returnedLength *uint32,
191 | process uintptr,
192 | flags uint32,
193 | ) uint32 {
194 | r1, _, _ := syscall.SyscallN(getSystemCpuSetInformation.Addr(),
195 | uintptr(unsafe.Pointer(information)),
196 | uintptr(bufferLength),
197 | uintptr(unsafe.Pointer(returnedLength)),
198 | process,
199 | uintptr(flags),
200 | )
201 | return uint32(r1)
202 | }
203 |
--------------------------------------------------------------------------------
/ntdll.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "log"
7 | "os"
8 | "syscall"
9 | "unicode/utf16"
10 | "unicode/utf8"
11 | "unsafe"
12 |
13 | "golang.org/x/sys/windows"
14 | )
15 |
16 | var (
17 | // Library
18 | libNtdll *windows.LazyDLL
19 |
20 | // Functions
21 | ntQueryInformationProcess *windows.LazyProc
22 | ntQuerySystemInformationEx *windows.LazyProc
23 | ntQueryKey *windows.LazyProc
24 | )
25 |
26 | func init() {
27 | // Library
28 | libNtdll = windows.NewLazySystemDLL("ntdll.dll")
29 |
30 | // Functions
31 | ntQueryKey = libNtdll.NewProc("NtQueryKey")
32 | ntQueryInformationProcess = libNtdll.NewProc("NtQueryInformationProcess")
33 | ntQuerySystemInformationEx = libNtdll.NewProc("NtQuerySystemInformationEx")
34 | }
35 |
36 | // The KeyInformationClass constants have been derived from the KEY_INFORMATION_CLASS enum definition.
37 | type KeyInformationClass uint32
38 |
39 | const (
40 | KeyBasicInformation KeyInformationClass = iota
41 | KeyNodeInformation
42 | KeyFullInformation
43 | KeyNameInformation
44 | KeyCachedInformation
45 | KeyFlagsInformation
46 | KeyVirtualizationInformation
47 | KeyHandleTagsInformation
48 | MaxKeyInfoClass
49 | )
50 |
51 | const STATUS_BUFFER_TOO_SMALL = 0xC0000023
52 |
53 | // OUT-parameter: KeyInformation, ResultLength.
54 | // *OPT-parameter: KeyInformation.
55 | func NtQueryKey(
56 | keyHandle uintptr,
57 | keyInformationClass KeyInformationClass,
58 | keyInformation *byte,
59 | length uint32,
60 | resultLength *uint32,
61 | ) int {
62 | r0, _, _ := ntQueryKey.Call(keyHandle,
63 | uintptr(keyInformationClass),
64 | uintptr(unsafe.Pointer(keyInformation)),
65 | uintptr(length),
66 | uintptr(unsafe.Pointer(resultLength)))
67 | return int(r0)
68 | }
69 |
70 | func GetRegistryLocation(regHandle uintptr) (string, error) {
71 | var size uint32 = 0
72 | result := NtQueryKey(regHandle, KeyNameInformation, nil, 0, &size)
73 | if result == STATUS_BUFFER_TOO_SMALL {
74 | buf := make([]byte, size)
75 | if result := NtQueryKey(regHandle, KeyNameInformation, &buf[0], size, &size); result == 0 {
76 | regPath, err := DecodeUTF16(buf)
77 | if err != nil {
78 | log.Println(err)
79 | }
80 |
81 | tempRegPath := replaceRegistryMachine(regPath)
82 | tempRegPath = generalizeControlSet(tempRegPath)
83 |
84 | return `HKEY_LOCAL_MACHINE\` + tempRegPath, nil
85 | }
86 | } else {
87 | return "", fmt.Errorf("error: 0x%X", result)
88 | }
89 | return "", nil
90 | }
91 |
92 | func DecodeUTF16(b []byte) (string, error) {
93 | if len(b)%2 != 0 {
94 | return "", fmt.Errorf("must have even length byte slice")
95 | }
96 |
97 | u16s := make([]uint16, 1)
98 | ret := &bytes.Buffer{}
99 | b8buf := make([]byte, 4)
100 |
101 | lb := len(b)
102 | for i := 0; i < lb; i += 2 {
103 | u16s[0] = uint16(b[i]) + (uint16(b[i+1]) << 8)
104 | r := utf16.Decode(u16s)
105 | n := utf8.EncodeRune(b8buf, r[0])
106 | ret.Write(b8buf[:n])
107 | }
108 |
109 | return ret.String(), nil
110 | }
111 |
112 | // NtQueryInformationProcess is a wrapper for ntdll.NtQueryInformationProcess.
113 | // The handle must have the PROCESS_QUERY_INFORMATION access right.
114 | // Returns an error of type NTStatus.
115 | func NtQueryInformationProcess(
116 | processHandle windows.Handle,
117 | processInformationClass int32,
118 | processInformation windows.Pointer,
119 | processInformationLength uint32,
120 | returnLength *uint32,
121 | ) error {
122 | r1, _, err := ntQueryInformationProcess.Call(
123 | uintptr(processHandle),
124 | uintptr(processInformationClass),
125 | uintptr(unsafe.Pointer(processInformation)),
126 | uintptr(processInformationLength),
127 | uintptr(unsafe.Pointer(returnLength)))
128 | if int(r1) < 0 {
129 | return os.NewSyscallError("NtQueryInformationProcess", err)
130 | }
131 | return nil
132 | }
133 |
134 | func NtQuerySystemInformationEx(
135 | systemInformationClass int32,
136 | inputBuffer unsafe.Pointer,
137 | inputBufferLength uint32,
138 | systemInformation *uint64,
139 | systemInformationLength uint32,
140 | returnLength *uint32,
141 | ) uint32 {
142 | r1, _, _ := syscall.SyscallN(ntQuerySystemInformationEx.Addr(),
143 | uintptr(systemInformationClass),
144 | uintptr(inputBuffer),
145 | uintptr(inputBufferLength),
146 | uintptr(unsafe.Pointer(systemInformation)),
147 | uintptr(systemInformationLength),
148 | uintptr(unsafe.Pointer(returnLength)),
149 | )
150 | return uint32(r1)
151 | }
152 |
--------------------------------------------------------------------------------
/reg.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "strings"
6 |
7 | "golang.org/x/sys/windows/registry"
8 | )
9 |
10 | func clen(n []byte) int {
11 | for i := len(n) - 1; i >= 0; i-- {
12 | if n[i] != 0 {
13 | return i + 1
14 | }
15 | }
16 | return len(n)
17 | }
18 |
19 | func GetStringValue(key registry.Key, name string) string {
20 | value, _, err := key.GetStringValue(name)
21 | if err != nil {
22 | return ""
23 | }
24 | return value
25 | }
26 |
27 | func GetBinaryValue(key registry.Key, name string) []byte {
28 | value, _, err := key.GetBinaryValue(name)
29 | if err != nil {
30 | return []byte{}
31 | }
32 | return value
33 | }
34 |
35 | func GetDWORDuint32Value(key registry.Key, name string) uint32 {
36 | buf := make([]byte, 4)
37 | key.GetValue(name, buf)
38 | return btoi32(buf)
39 | }
40 |
41 | func setMSIMode(item *Device) {
42 | var k registry.Key
43 | var err error
44 | if item.MsiSupported == 1 {
45 | k, _, err = registry.CreateKey(item.reg, `Interrupt Management\MessageSignaledInterruptProperties`, registry.ALL_ACCESS)
46 | if err != nil {
47 | log.Println(err)
48 | }
49 | if err := k.SetDWordValue("MSISupported", 1); err != nil {
50 | log.Println(err)
51 | }
52 |
53 | if item.MessageNumberLimit == 0 {
54 | if err := k.DeleteValue("MessageNumberLimit"); err != nil {
55 | log.Println(err)
56 | }
57 | } else {
58 | if err := k.SetDWordValue("MessageNumberLimit", uint32(item.MessageNumberLimit)); err != nil {
59 | log.Println(err)
60 | }
61 | }
62 | } else {
63 | k, err = registry.OpenKey(item.reg, `Interrupt Management\MessageSignaledInterruptProperties`, registry.ALL_ACCESS)
64 | if err != nil {
65 | log.Println(err)
66 | }
67 |
68 | if err := registry.DeleteKey(item.reg, `Interrupt Management\MessageSignaledInterruptProperties`); err != nil {
69 | log.Println(err)
70 | }
71 | }
72 | if err := k.Close(); err != nil {
73 | log.Println(err)
74 | }
75 | }
76 |
77 | func setAffinityPolicy(item *Device) {
78 | var k registry.Key
79 | var err error
80 |
81 | if item.DevicePolicy == 0 && item.DevicePriority == 0 {
82 |
83 | k, err = registry.OpenKey(item.reg, `Interrupt Management\Affinity Policy`, registry.ALL_ACCESS)
84 | if err != nil {
85 | log.Println(err)
86 | }
87 |
88 | if err := registry.DeleteKey(item.reg, `Interrupt Management\Affinity Policy`); err != nil {
89 | log.Println(err)
90 | }
91 |
92 | } else {
93 |
94 | k, _, err = registry.CreateKey(item.reg, `Interrupt Management\Affinity Policy`, registry.ALL_ACCESS)
95 | if err != nil {
96 | log.Println(err)
97 | }
98 |
99 | if err := k.SetDWordValue("DevicePolicy", item.DevicePolicy); err != nil {
100 | log.Println(err)
101 | }
102 |
103 | if item.DevicePolicy != 4 {
104 | k.DeleteValue("AssignmentSetOverride")
105 | }
106 |
107 | if item.DevicePriority == 0 {
108 | k.DeleteValue("DevicePriority")
109 | } else if err := k.SetDWordValue("DevicePriority", item.DevicePriority); err != nil {
110 | log.Println(err)
111 | }
112 |
113 | AssignmentSetOverrideByte := i64tob(uint64(item.AssignmentSetOverride))
114 | if err := k.SetBinaryValue("AssignmentSetOverride", AssignmentSetOverrideByte[:clen(AssignmentSetOverrideByte)]); err != nil {
115 | log.Println(err)
116 | }
117 |
118 | }
119 | if err := k.Close(); err != nil {
120 | log.Println(err)
121 | }
122 | }
123 |
124 | // \REGISTRY\MACHINE\
125 | func replaceRegistryMachine(regPath string) string {
126 | indexMACHINE := strings.Index(regPath, "\\REGISTRY\\MACHINE\\")
127 | if indexMACHINE == -1 {
128 | log.Println("not Found")
129 | return ""
130 | }
131 | return regPath[indexMACHINE+len("\\REGISTRY\\MACHINE\\"):]
132 | }
133 |
134 | // replaces ControlSet00X with CurrentControlSet
135 | func generalizeControlSet(regPath string) string {
136 | // https://learn.microsoft.com/en-us/windows-hardware/drivers/install/hklm-system-currentcontrolset-control-registry-tree
137 |
138 | regPathArray := strings.Split(regPath, "\\")
139 | for i := 0; i < len(regPathArray); i++ {
140 | if strings.HasPrefix(regPathArray[i], "ControlSet00") {
141 | regPathArray[i] = "CurrentControlSet"
142 | return strings.Join(regPathArray, "\\")
143 | }
144 | }
145 | return strings.Join(regPathArray, "\\")
146 |
147 | }
148 |
--------------------------------------------------------------------------------
/rsrc.syso:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spddl/GoInterruptPolicy/6784443a10e08cb8105ef43165f0d466e803ea27/rsrc.syso
--------------------------------------------------------------------------------
/run.bat:
--------------------------------------------------------------------------------
1 | @echo off
2 |
3 | SET filename=GoInterruptPolicy
4 |
5 | net session >nul 2>&1
6 | set nilIsAdmin=%errorLevel%
7 |
8 | :loop
9 | cls
10 |
11 | if %nilIsAdmin% EQU 0 (
12 | go run -tags debug -buildvcs=false .
13 | ) else (
14 | go build -tags debug -buildvcs=false -o %filename%_debug.exe
15 | IF %ERRORLEVEL% EQU 0 %filename%_debug.exe
16 | )
17 |
18 | @REM IF %ERRORLEVEL% EQU 0 %filename%.exe -devobj \Device\NTPNP_PCI0015 -policy 4 -cpu 1,2,3 -restart
19 | @REM IF %ERRORLEVEL% EQU 0 %filename%.exe -devobj \Device\NTPNP_PCI0015 -policy 4 -cpu 1,2,3,4 -restart-on-change
20 | @REM IF %ERRORLEVEL% EQU 0 %filename%.exe -devobj \Device\NTPNP_PCI0015 -msisupported 0
21 |
22 | pause
23 | goto loop
24 |
--------------------------------------------------------------------------------
/setupapi_windows.go:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: MIT
2 | *
3 | * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
4 | */
5 |
6 | package main
7 |
8 | import (
9 | "encoding/binary"
10 | "errors"
11 | "fmt"
12 | "syscall"
13 | "unsafe"
14 |
15 | "golang.org/x/sys/windows"
16 | "golang.org/x/sys/windows/registry"
17 | )
18 |
19 | // sys setupDiCreateDeviceInfoListEx(classGUID *windows.GUID, hwndParent uintptr, machineName *uint16, reserved uintptr) (handle DevInfo, err error) [failretval==DevInfo(windows.InvalidHandle)] = setupapi.SetupDiCreateDeviceInfoListExW
20 |
21 | // SetupDiCreateDeviceInfoListEx function creates an empty device information set on a remote or a local computer and optionally associates the set with a device setup class.
22 | func SetupDiCreateDeviceInfoListEx(classGUID *windows.GUID, hwndParent uintptr, machineName string) (deviceInfoSet DevInfo, err error) {
23 | var machineNameUTF16 *uint16
24 | if machineName != "" {
25 | machineNameUTF16, err = syscall.UTF16PtrFromString(machineName)
26 | if err != nil {
27 | return
28 | }
29 | }
30 | return setupDiCreateDeviceInfoListEx(classGUID, hwndParent, machineNameUTF16, 0)
31 | }
32 |
33 | // sys setupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo, deviceInfoSetDetailData *DevInfoListDetailData) (err error) = setupapi.SetupDiGetDeviceInfoListDetailW
34 |
35 | // SetupDiGetDeviceInfoListDetail function retrieves information associated with a device information set including the class GUID, remote computer handle, and remote computer name.
36 | func SetupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo) (deviceInfoSetDetailData *DevInfoListDetailData, err error) {
37 | data := &DevInfoListDetailData{}
38 | data.size = uint32(unsafe.Sizeof(*data))
39 |
40 | return data, setupDiGetDeviceInfoListDetail(deviceInfoSet, data)
41 | }
42 |
43 | // GetDeviceInfoListDetail method retrieves information associated with a device information set including the class GUID, remote computer handle, and remote computer name.
44 | func (deviceInfoSet DevInfo) GetDeviceInfoListDetail() (*DevInfoListDetailData, error) {
45 | return SetupDiGetDeviceInfoListDetail(deviceInfoSet)
46 | }
47 |
48 | // sys setupDiCreateDeviceInfo(deviceInfoSet DevInfo, DeviceName *uint16, classGUID *windows.GUID, DeviceDescription *uint16, hwndParent uintptr, CreationFlags DICD, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiCreateDeviceInfoW
49 |
50 | // SetupDiCreateDeviceInfo function creates a new device information element and adds it as a new member to the specified device information set.
51 | func SetupDiCreateDeviceInfo(deviceInfoSet DevInfo, deviceName string, classGUID *windows.GUID, deviceDescription string, hwndParent uintptr, creationFlags DICD) (deviceInfoData *DevInfoData, err error) {
52 | deviceNameUTF16, err := syscall.UTF16PtrFromString(deviceName)
53 | if err != nil {
54 | return
55 | }
56 |
57 | var deviceDescriptionUTF16 *uint16
58 | if deviceDescription != "" {
59 | deviceDescriptionUTF16, err = syscall.UTF16PtrFromString(deviceDescription)
60 | if err != nil {
61 | return
62 | }
63 | }
64 |
65 | data := &DevInfoData{}
66 | data.size = uint32(unsafe.Sizeof(*data))
67 |
68 | return data, setupDiCreateDeviceInfo(deviceInfoSet, deviceNameUTF16, classGUID, deviceDescriptionUTF16, hwndParent, creationFlags, data)
69 | }
70 |
71 | // CreateDeviceInfo method creates a new device information element and adds it as a new member to the specified device information set.
72 | func (deviceInfoSet DevInfo) CreateDeviceInfo(deviceName string, classGUID *windows.GUID, deviceDescription string, hwndParent uintptr, creationFlags DICD) (*DevInfoData, error) {
73 | return SetupDiCreateDeviceInfo(deviceInfoSet, deviceName, classGUID, deviceDescription, hwndParent, creationFlags)
74 | }
75 |
76 | // sys setupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex uint32, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiEnumDeviceInfo
77 |
78 | // SetupDiEnumDeviceInfo function returns a DevInfoData structure that specifies a device information element in a device information set.
79 | func SetupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex int) (*DevInfoData, error) {
80 | data := &DevInfoData{}
81 | data.size = uint32(unsafe.Sizeof(*data))
82 |
83 | return data, setupDiEnumDeviceInfo(deviceInfoSet, uint32(memberIndex), data)
84 | }
85 |
86 | // EnumDeviceInfo method returns a DevInfoData structure that specifies a device information element in a device information set.
87 | func (deviceInfoSet DevInfo) EnumDeviceInfo(memberIndex int) (*DevInfoData, error) {
88 | return SetupDiEnumDeviceInfo(deviceInfoSet, memberIndex)
89 | }
90 |
91 | // SetupDiDestroyDeviceInfoList function deletes a device information set and frees all associated memory.
92 | // sys SetupDiDestroyDeviceInfoList(deviceInfoSet DevInfo) (err error) = setupapi.SetupDiDestroyDeviceInfoList
93 |
94 | // Close method deletes a device information set and frees all associated memory.
95 | func (deviceInfoSet DevInfo) Close() error {
96 | return SetupDiDestroyDeviceInfoList(deviceInfoSet)
97 | }
98 |
99 | // sys SetupDiBuildDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) = setupapi.SetupDiBuildDriverInfoList
100 |
101 | // BuildDriverInfoList method builds a list of drivers that is associated with a specific device or with the global class driver list for a device information set.
102 | func (deviceInfoSet DevInfo) BuildDriverInfoList(deviceInfoData *DevInfoData, driverType SPDIT) error {
103 | return SetupDiBuildDriverInfoList(deviceInfoSet, deviceInfoData, driverType)
104 | }
105 |
106 | // sys SetupDiCancelDriverInfoSearch(deviceInfoSet DevInfo) (err error) = setupapi.SetupDiCancelDriverInfoSearch
107 |
108 | // CancelDriverInfoSearch method cancels a driver list search that is currently in progress in a different thread.
109 | func (deviceInfoSet DevInfo) CancelDriverInfoSearch() error {
110 | return SetupDiCancelDriverInfoSearch(deviceInfoSet)
111 | }
112 |
113 | // sys setupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT, memberIndex uint32, driverInfoData *DrvInfoData) (err error) = setupapi.SetupDiEnumDriverInfoW
114 |
115 | // SetupDiEnumDriverInfo function enumerates the members of a driver list.
116 | func SetupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT, memberIndex int) (*DrvInfoData, error) {
117 | data := &DrvInfoData{}
118 | data.size = uint32(unsafe.Sizeof(*data))
119 |
120 | return data, setupDiEnumDriverInfo(deviceInfoSet, deviceInfoData, driverType, uint32(memberIndex), data)
121 | }
122 |
123 | // EnumDriverInfo method enumerates the members of a driver list.
124 | func (deviceInfoSet DevInfo) EnumDriverInfo(deviceInfoData *DevInfoData, driverType SPDIT, memberIndex int) (*DrvInfoData, error) {
125 | return SetupDiEnumDriverInfo(deviceInfoSet, deviceInfoData, driverType, memberIndex)
126 | }
127 |
128 | // sys setupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) = setupapi.SetupDiGetSelectedDriverW
129 |
130 | // SetupDiGetSelectedDriver function retrieves the selected driver for a device information set or a particular device information element.
131 | func SetupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (*DrvInfoData, error) {
132 | data := &DrvInfoData{}
133 | data.size = uint32(unsafe.Sizeof(*data))
134 |
135 | return data, setupDiGetSelectedDriver(deviceInfoSet, deviceInfoData, data)
136 | }
137 |
138 | // GetSelectedDriver method retrieves the selected driver for a device information set or a particular device information element.
139 | func (deviceInfoSet DevInfo) GetSelectedDriver(deviceInfoData *DevInfoData) (*DrvInfoData, error) {
140 | return SetupDiGetSelectedDriver(deviceInfoSet, deviceInfoData)
141 | }
142 |
143 | // sys SetupDiSetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) = setupapi.SetupDiSetSelectedDriverW
144 |
145 | // SetSelectedDriver method sets, or resets, the selected driver for a device information element or the selected class driver for a device information set.
146 | func (deviceInfoSet DevInfo) SetSelectedDriver(deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) error {
147 | return SetupDiSetSelectedDriver(deviceInfoSet, deviceInfoData, driverInfoData)
148 | }
149 |
150 | // sys setupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData, driverInfoDetailData *DrvInfoDetailData, driverInfoDetailDataSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetDriverInfoDetailW
151 |
152 | // SetupDiGetDriverInfoDetail function retrieves driver information detail for a device information set or a particular device information element in the device information set.
153 | func SetupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (*DrvInfoDetailData, error) {
154 | const bufCapacity = 0x800
155 | buf := [bufCapacity]byte{}
156 | var bufLen uint32
157 |
158 | data := (*DrvInfoDetailData)(unsafe.Pointer(&buf[0]))
159 | data.size = uint32(unsafe.Sizeof(*data))
160 |
161 | err := setupDiGetDriverInfoDetail(deviceInfoSet, deviceInfoData, driverInfoData, data, bufCapacity, &bufLen)
162 | if err == nil {
163 | // The buffer was was sufficiently big.
164 | data.size = bufLen
165 | return data, nil
166 | }
167 |
168 | if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_INSUFFICIENT_BUFFER {
169 | // The buffer was too small. Now that we got the required size, create another one big enough and retry.
170 | buf := make([]byte, bufLen)
171 | data := (*DrvInfoDetailData)(unsafe.Pointer(&buf[0]))
172 | data.size = uint32(unsafe.Sizeof(*data))
173 |
174 | err = setupDiGetDriverInfoDetail(deviceInfoSet, deviceInfoData, driverInfoData, data, bufLen, &bufLen)
175 | if err == nil {
176 | data.size = bufLen
177 | return data, nil
178 | }
179 | }
180 |
181 | return nil, err
182 | }
183 |
184 | // GetDriverInfoDetail method retrieves driver information detail for a device information set or a particular device information element in the device information set.
185 | func (deviceInfoSet DevInfo) GetDriverInfoDetail(deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (*DrvInfoDetailData, error) {
186 | return SetupDiGetDriverInfoDetail(deviceInfoSet, deviceInfoData, driverInfoData)
187 | }
188 |
189 | // sys SetupDiDestroyDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) = setupapi.SetupDiDestroyDriverInfoList
190 |
191 | // DestroyDriverInfoList method deletes a driver list.
192 | func (deviceInfoSet DevInfo) DestroyDriverInfoList(deviceInfoData *DevInfoData, driverType SPDIT) error {
193 | return SetupDiDestroyDriverInfoList(deviceInfoSet, deviceInfoData, driverType)
194 | }
195 |
196 | // sys setupDiGetClassDevsEx(classGUID *windows.GUID, Enumerator *uint16, hwndParent uintptr, Flags DIGCF, deviceInfoSet DevInfo, machineName *uint16, reserved uintptr) (handle DevInfo, err error) [failretval==DevInfo(windows.InvalidHandle)] = setupapi.SetupDiGetClassDevsExW
197 |
198 | // SetupDiGetClassDevsEx function returns a handle to a device information set that contains requested device information elements for a local or a remote computer.
199 | func SetupDiGetClassDevsEx(classGUID *windows.GUID, enumerator string, hwndParent uintptr, flags DIGCF, deviceInfoSet DevInfo, machineName string) (handle DevInfo, err error) {
200 | var enumeratorUTF16 *uint16
201 | if enumerator != "" {
202 | enumeratorUTF16, err = syscall.UTF16PtrFromString(enumerator)
203 | if err != nil {
204 | return
205 | }
206 | }
207 | var machineNameUTF16 *uint16
208 | if machineName != "" {
209 | machineNameUTF16, err = syscall.UTF16PtrFromString(machineName)
210 | if err != nil {
211 | return
212 | }
213 | }
214 | return setupDiGetClassDevsEx(classGUID, enumeratorUTF16, hwndParent, flags, deviceInfoSet, machineNameUTF16, 0)
215 | }
216 |
217 | // SetupDiCallClassInstaller function calls the appropriate class installer, and any registered co-installers, with the specified installation request (DIF code).
218 | // sys SetupDiCallClassInstaller(installFunction DI_FUNCTION, deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiCallClassInstaller
219 |
220 | // CallClassInstaller member calls the appropriate class installer, and any registered co-installers, with the specified installation request (DIF code).
221 | func (deviceInfoSet DevInfo) CallClassInstaller(installFunction DI_FUNCTION, deviceInfoData *DevInfoData) error {
222 | return SetupDiCallClassInstaller(installFunction, deviceInfoSet, deviceInfoData)
223 | }
224 |
225 | // sys setupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, Scope DICS_FLAG, HwProfile uint32, KeyType DIREG, samDesired uint32) (key windows.Handle, err error) [failretval==windows.InvalidHandle] = setupapi.SetupDiOpenDevRegKey
226 |
227 | // SetupDiOpenDevRegKey function opens a registry key for device-specific configuration information.
228 | func SetupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, scope DICS_FLAG, hwProfile uint32, keyType DIREG, samDesired uint32) (registry.Key, error) {
229 | handle, err := setupDiOpenDevRegKey(deviceInfoSet, deviceInfoData, scope, hwProfile, keyType, samDesired)
230 | return registry.Key(handle), err
231 | }
232 |
233 | // OpenDevRegKey method opens a registry key for device-specific configuration information.
234 | func (deviceInfoSet DevInfo) OpenDevRegKey(deviceInfoData *DevInfoData, scope DICS_FLAG, hwProfile uint32, keyType DIREG, samDesired uint32) (registry.Key, error) {
235 | return SetupDiOpenDevRegKey(deviceInfoSet, deviceInfoData, scope, hwProfile, keyType, samDesired)
236 | }
237 |
238 | // GetInterfaceID method returns network interface ID.
239 | func (deviceInfoSet DevInfo) GetInterfaceID(deviceInfoData *DevInfoData) (*windows.GUID, error) {
240 | // Open HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\\ registry key.
241 | key, err := deviceInfoSet.OpenDevRegKey(deviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, registry.READ)
242 | if err != nil {
243 | return nil, errors.New("Device-specific registry key open failed: " + err.Error())
244 | }
245 | defer key.Close()
246 |
247 | // Read the NetCfgInstanceId value.
248 | value, valueType, err := key.GetStringValue("NetCfgInstanceId")
249 | if err != nil {
250 | return nil, errors.New("RegQueryStringValue(\"NetCfgInstanceId\") failed: " + err.Error())
251 | }
252 | if valueType != registry.SZ {
253 | return nil, fmt.Errorf("NetCfgInstanceId registry value is not REG_SZ (expected: %v, provided: %v)", registry.SZ, valueType)
254 | }
255 |
256 | // Convert to windows.GUID.
257 | ifid, err := FromString(value)
258 | if err != nil {
259 | return nil, fmt.Errorf("NetCfgInstanceId registry value is not a GUID (expected: \"{...}\", provided: %q)", value)
260 | }
261 |
262 | return ifid, nil
263 | }
264 |
265 | // sys setupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyRegDataType *uint32, propertyBuffer *byte, propertyBufferSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetDeviceRegistryPropertyW
266 |
267 | // SetupDiGetDeviceRegistryProperty function retrieves a specified Plug and Play device property.
268 | func SetupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP) (value interface{}, err error) {
269 | buf := make([]byte, 0x100)
270 | var dataType, bufLen uint32
271 | err = setupDiGetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, &dataType, &buf[0], uint32(cap(buf)), &bufLen)
272 | if err == nil {
273 | // The buffer was sufficiently big.
274 | return getRegistryValue(buf[:bufLen], dataType)
275 | }
276 |
277 | if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_INSUFFICIENT_BUFFER {
278 | // The buffer was too small. Now that we got the required size, create another one big enough and retry.
279 | buf = make([]byte, bufLen)
280 | err = setupDiGetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, &dataType, &buf[0], uint32(cap(buf)), &bufLen)
281 | if err == nil {
282 | return getRegistryValue(buf[:bufLen], dataType)
283 | }
284 | }
285 |
286 | return
287 | }
288 |
289 | func getRegistryValue(buf []byte, dataType uint32) (interface{}, error) {
290 | switch dataType {
291 | case windows.REG_SZ:
292 | return windows.UTF16ToString(BufToUTF16(buf)), nil
293 | case windows.REG_EXPAND_SZ:
294 | return registry.ExpandString(windows.UTF16ToString(BufToUTF16(buf)))
295 | case windows.REG_BINARY:
296 | return buf, nil
297 | case windows.REG_DWORD_LITTLE_ENDIAN:
298 | return binary.LittleEndian.Uint32(buf), nil
299 | case windows.REG_DWORD_BIG_ENDIAN:
300 | return binary.BigEndian.Uint32(buf), nil
301 | case windows.REG_MULTI_SZ:
302 | bufW := BufToUTF16(buf)
303 | a := []string{}
304 | for i := 0; i < len(bufW); {
305 | j := i + wcslen(bufW[i:])
306 | if i < j {
307 | a = append(a, windows.UTF16ToString(bufW[i:j]))
308 | }
309 | i = j + 1
310 | }
311 | return a, nil
312 | case windows.REG_QWORD_LITTLE_ENDIAN:
313 | return binary.LittleEndian.Uint64(buf), nil
314 | default:
315 | return nil, fmt.Errorf("unsupported registry value type: %v", dataType)
316 | }
317 | }
318 |
319 | // BufToUTF16 function reinterprets []byte buffer as []uint16
320 | func BufToUTF16(buf []byte) []uint16 {
321 | sl := struct {
322 | addr *uint16
323 | len int
324 | cap int
325 | }{(*uint16)(unsafe.Pointer(&buf[0])), len(buf) / 2, cap(buf) / 2}
326 | return *(*[]uint16)(unsafe.Pointer(&sl))
327 | }
328 |
329 | // UTF16ToBuf function reinterprets []uint16 as []byte
330 | func UTF16ToBuf(buf []uint16) []byte {
331 | sl := struct {
332 | addr *byte
333 | len int
334 | cap int
335 | }{(*byte)(unsafe.Pointer(&buf[0])), len(buf) * 2, cap(buf) * 2}
336 | return *(*[]byte)(unsafe.Pointer(&sl))
337 | }
338 |
339 | func wcslen(str []uint16) int {
340 | for i := 0; i < len(str); i++ {
341 | if str[i] == 0 {
342 | return i
343 | }
344 | }
345 | return len(str)
346 | }
347 |
348 | // GetDeviceRegistryProperty method retrieves a specified Plug and Play device property.
349 | func (deviceInfoSet DevInfo) GetDeviceRegistryProperty(deviceInfoData *DevInfoData, property SPDRP) (interface{}, error) {
350 | return SetupDiGetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property)
351 | }
352 |
353 | // sys setupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyBuffer *byte, propertyBufferSize uint32) (err error) = setupapi.SetupDiSetDeviceRegistryPropertyW
354 |
355 | // SetupDiSetDeviceRegistryProperty function sets a Plug and Play device property for a device.
356 | func SetupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyBuffers []byte) error {
357 | return setupDiSetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, &propertyBuffers[0], uint32(len(propertyBuffers)))
358 | }
359 |
360 | // SetDeviceRegistryProperty function sets a Plug and Play device property for a device.
361 | func (deviceInfoSet DevInfo) SetDeviceRegistryProperty(deviceInfoData *DevInfoData, property SPDRP, propertyBuffers []byte) error {
362 | return SetupDiSetDeviceRegistryProperty(deviceInfoSet, deviceInfoData, property, propertyBuffers)
363 | }
364 |
365 | // sys setupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) = setupapi.SetupDiGetDeviceInstallParamsW
366 |
367 | // SetupDiGetDeviceInstallParams function retrieves device installation parameters for a device information set or a particular device information element.
368 | func SetupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (*DevInstallParams, error) {
369 | params := &DevInstallParams{}
370 | params.size = uint32(unsafe.Sizeof(*params))
371 |
372 | return params, setupDiGetDeviceInstallParams(deviceInfoSet, deviceInfoData, params)
373 | }
374 |
375 | // GetDeviceInstallParams method retrieves device installation parameters for a device information set or a particular device information element.
376 | func (deviceInfoSet DevInfo) GetDeviceInstallParams(deviceInfoData *DevInfoData) (*DevInstallParams, error) {
377 | return SetupDiGetDeviceInstallParams(deviceInfoSet, deviceInfoData)
378 | }
379 |
380 | // SetupDiGetClassInstallParams function retrieves class installation parameters for a device information set or a particular device information element.
381 | // sys SetupDiGetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32, requiredSize *uint32) (err error) = setupapi.SetupDiGetClassInstallParamsW
382 |
383 | // GetClassInstallParams method retrieves class installation parameters for a device information set or a particular device information element.
384 | func (deviceInfoSet DevInfo) GetClassInstallParams(deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32, requiredSize *uint32) error {
385 | return SetupDiGetClassInstallParams(deviceInfoSet, deviceInfoData, classInstallParams, classInstallParamsSize, requiredSize)
386 | }
387 |
388 | // sys SetupDiSetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) = setupapi.SetupDiSetDeviceInstallParamsW
389 |
390 | // SetDeviceInstallParams member sets device installation parameters for a device information set or a particular device information element.
391 | func (deviceInfoSet DevInfo) SetDeviceInstallParams(deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) error {
392 | return SetupDiSetDeviceInstallParams(deviceInfoSet, deviceInfoData, deviceInstallParams)
393 | }
394 |
395 | // SetupDiSetClassInstallParams function sets or clears class install parameters for a device information set or a particular device information element.
396 | // sys SetupDiSetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32) (err error) = setupapi.SetupDiSetClassInstallParamsW
397 |
398 | // SetClassInstallParams method sets or clears class install parameters for a device information set or a particular device information element.
399 | func (deviceInfoSet DevInfo) SetClassInstallParams(deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32) error {
400 | return SetupDiSetClassInstallParams(deviceInfoSet, deviceInfoData, classInstallParams, classInstallParamsSize)
401 | }
402 |
403 | // sys setupDiClassNameFromGuidEx(classGUID *windows.GUID, className *uint16, classNameSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) = setupapi.SetupDiClassNameFromGuidExW
404 |
405 | // SetupDiClassNameFromGuidEx function retrieves the class name associated with a class GUID. The class can be installed on a local or remote computer.
406 | func SetupDiClassNameFromGuidEx(classGUID *windows.GUID, machineName string) (className string, err error) {
407 | var classNameUTF16 [MAX_CLASS_NAME_LEN]uint16
408 |
409 | var machineNameUTF16 *uint16
410 | if machineName != "" {
411 | machineNameUTF16, err = syscall.UTF16PtrFromString(machineName)
412 | if err != nil {
413 | return
414 | }
415 | }
416 |
417 | err = setupDiClassNameFromGuidEx(classGUID, &classNameUTF16[0], MAX_CLASS_NAME_LEN, nil, machineNameUTF16, 0)
418 | if err != nil {
419 | return
420 | }
421 |
422 | className = windows.UTF16ToString(classNameUTF16[:])
423 | return
424 | }
425 |
426 | // sys setupDiClassGuidsFromNameEx(className *uint16, classGuidList *windows.GUID, classGuidListSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) = setupapi.SetupDiClassGuidsFromNameExW
427 |
428 | // SetupDiClassGuidsFromNameEx function retrieves the GUIDs associated with the specified class name. This resulting list contains the classes currently installed on a local or remote computer.
429 | func SetupDiClassGuidsFromNameEx(className, machineName string) ([]windows.GUID, error) {
430 | classNameUTF16, err := syscall.UTF16PtrFromString(className)
431 | if err != nil {
432 | return nil, err
433 | }
434 |
435 | const bufCapacity = 4
436 | var buf [bufCapacity]windows.GUID
437 | var bufLen uint32
438 |
439 | var machineNameUTF16 *uint16
440 | if machineName != "" {
441 | machineNameUTF16, err = syscall.UTF16PtrFromString(machineName)
442 | if err != nil {
443 | return nil, err
444 | }
445 | }
446 |
447 | err = setupDiClassGuidsFromNameEx(classNameUTF16, &buf[0], bufCapacity, &bufLen, machineNameUTF16, 0)
448 | if err == nil {
449 | // The GUID array was sufficiently big. Return its slice.
450 | return buf[:bufLen], nil
451 | }
452 |
453 | if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_INSUFFICIENT_BUFFER {
454 | // The GUID array was too small. Now that we got the required size, create another one big enough and retry.
455 | buf := make([]windows.GUID, bufLen)
456 | err = setupDiClassGuidsFromNameEx(classNameUTF16, &buf[0], bufLen, &bufLen, machineNameUTF16, 0)
457 | if err == nil {
458 | return buf[:bufLen], nil
459 | }
460 | }
461 |
462 | return nil, err
463 | }
464 |
465 | // sys setupDiGetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiGetSelectedDevice
466 |
467 | // SetupDiGetSelectedDevice function retrieves the selected device information element in a device information set.
468 | func SetupDiGetSelectedDevice(deviceInfoSet DevInfo) (*DevInfoData, error) {
469 | data := &DevInfoData{}
470 | data.size = uint32(unsafe.Sizeof(*data))
471 |
472 | return data, setupDiGetSelectedDevice(deviceInfoSet, data)
473 | }
474 |
475 | // GetSelectedDevice method retrieves the selected device information element in a device information set.
476 | func (deviceInfoSet DevInfo) GetSelectedDevice() (*DevInfoData, error) {
477 | return SetupDiGetSelectedDevice(deviceInfoSet)
478 | }
479 |
480 | // SetupDiSetSelectedDevice function sets a device information element as the selected member of a device information set. This function is typically used by an installation wizard.
481 | // sys SetupDiSetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) = setupapi.SetupDiSetSelectedDevice
482 |
483 | // SetSelectedDevice method sets a device information element as the selected member of a device information set. This function is typically used by an installation wizard.
484 | func (deviceInfoSet DevInfo) SetSelectedDevice(deviceInfoData *DevInfoData) error {
485 | return SetupDiSetSelectedDevice(deviceInfoSet, deviceInfoData)
486 | }
487 |
--------------------------------------------------------------------------------
/types_windows.go:
--------------------------------------------------------------------------------
1 | /* SPDX-License-Identifier: MIT
2 | *
3 | * Copyright (C) 2019 WireGuard LLC. All Rights Reserved.
4 | */
5 |
6 | package main
7 |
8 | import (
9 | "strings"
10 | "syscall"
11 | "unsafe"
12 |
13 | "golang.org/x/sys/windows"
14 | )
15 |
16 | const (
17 | MAX_DEVICE_ID_LEN = 200
18 | MAX_DEVNODE_ID_LEN = MAX_DEVICE_ID_LEN
19 | MAX_GUID_STRING_LEN = 39 // 38 chars + terminator null
20 | MAX_CLASS_NAME_LEN = 32
21 | MAX_PROFILE_LEN = 80
22 | MAX_CONFIG_VALUE = 9999
23 | MAX_INSTANCE_VALUE = 9999
24 | CONFIGMG_VERSION = 0x0400
25 | )
26 |
27 | // Define maximum string length constants
28 | const (
29 | LINE_LEN = 256 // Windows 9x-compatible maximum for displayable strings coming from a device INF.
30 | MAX_INF_STRING_LENGTH = 4096 // Actual maximum size of an INF string (including string substitutions).
31 | MAX_INF_SECTION_NAME_LENGTH = 255 // For Windows 9x compatibility, INF section names should be constrained to 32 characters.
32 | MAX_TITLE_LEN = 60
33 | MAX_INSTRUCTION_LEN = 256
34 | MAX_LABEL_LEN = 30
35 | MAX_SERVICE_NAME_LEN = 256
36 | MAX_SUBTITLE_LEN = 256
37 | )
38 |
39 | const (
40 | // SP_MAX_MACHINENAME_LENGTH defines maximum length of a machine name in the format expected by ConfigMgr32 CM_Connect_Machine (i.e., "\\\\MachineName\0").
41 | SP_MAX_MACHINENAME_LENGTH = windows.MAX_PATH + 3
42 | )
43 |
44 | // HSPFILEQ is type for setup file queue
45 | type HSPFILEQ uintptr
46 |
47 | // DevInfo holds reference to device information set
48 | type DevInfo windows.Handle
49 |
50 | // DevInfoData is a device information structure (references a device instance that is a member of a device information set)
51 | type DevInfoData struct {
52 | size uint32
53 | ClassGUID windows.GUID
54 | DevInst uint32 // DEVINST handle
55 | _ uintptr
56 | }
57 |
58 | // DevInfoListDetailData is a structure for detailed information on a device information set (used for SetupDiGetDeviceInfoListDetail which supercedes the functionality of SetupDiGetDeviceInfoListClass).
59 | type DevInfoListDetailData struct {
60 | size uint32
61 | ClassGUID windows.GUID
62 | RemoteMachineHandle windows.Handle
63 | remoteMachineName [SP_MAX_MACHINENAME_LENGTH]uint16
64 | }
65 |
66 | func (data *DevInfoListDetailData) GetRemoteMachineName() string {
67 | return windows.UTF16ToString(data.remoteMachineName[:])
68 | }
69 |
70 | func (data *DevInfoListDetailData) SetRemoteMachineName(remoteMachineName string) error {
71 | str, err := syscall.UTF16FromString(remoteMachineName)
72 | if err != nil {
73 | return err
74 | }
75 | copy(data.remoteMachineName[:], str)
76 | return nil
77 | }
78 |
79 | // DI_FUNCTION is function type for device installer
80 | type DI_FUNCTION uint32
81 |
82 | const (
83 | DIF_SELECTDEVICE DI_FUNCTION = 0x00000001
84 | DIF_INSTALLDEVICE DI_FUNCTION = 0x00000002
85 | DIF_ASSIGNRESOURCES DI_FUNCTION = 0x00000003
86 | DIF_PROPERTIES DI_FUNCTION = 0x00000004
87 | DIF_REMOVE DI_FUNCTION = 0x00000005
88 | DIF_FIRSTTIMESETUP DI_FUNCTION = 0x00000006
89 | DIF_FOUNDDEVICE DI_FUNCTION = 0x00000007
90 | DIF_SELECTCLASSDRIVERS DI_FUNCTION = 0x00000008
91 | DIF_VALIDATECLASSDRIVERS DI_FUNCTION = 0x00000009
92 | DIF_INSTALLCLASSDRIVERS DI_FUNCTION = 0x0000000A
93 | DIF_CALCDISKSPACE DI_FUNCTION = 0x0000000B
94 | DIF_DESTROYPRIVATEDATA DI_FUNCTION = 0x0000000C
95 | DIF_VALIDATEDRIVER DI_FUNCTION = 0x0000000D
96 | DIF_DETECT DI_FUNCTION = 0x0000000F
97 | DIF_INSTALLWIZARD DI_FUNCTION = 0x00000010
98 | DIF_DESTROYWIZARDDATA DI_FUNCTION = 0x00000011
99 | DIF_PROPERTYCHANGE DI_FUNCTION = 0x00000012
100 | DIF_ENABLECLASS DI_FUNCTION = 0x00000013
101 | DIF_DETECTVERIFY DI_FUNCTION = 0x00000014
102 | DIF_INSTALLDEVICEFILES DI_FUNCTION = 0x00000015
103 | DIF_UNREMOVE DI_FUNCTION = 0x00000016
104 | DIF_SELECTBESTCOMPATDRV DI_FUNCTION = 0x00000017
105 | DIF_ALLOW_INSTALL DI_FUNCTION = 0x00000018
106 | DIF_REGISTERDEVICE DI_FUNCTION = 0x00000019
107 | DIF_NEWDEVICEWIZARD_PRESELECT DI_FUNCTION = 0x0000001A
108 | DIF_NEWDEVICEWIZARD_SELECT DI_FUNCTION = 0x0000001B
109 | DIF_NEWDEVICEWIZARD_PREANALYZE DI_FUNCTION = 0x0000001C
110 | DIF_NEWDEVICEWIZARD_POSTANALYZE DI_FUNCTION = 0x0000001D
111 | DIF_NEWDEVICEWIZARD_FINISHINSTALL DI_FUNCTION = 0x0000001E
112 | DIF_INSTALLINTERFACES DI_FUNCTION = 0x00000020
113 | DIF_DETECTCANCEL DI_FUNCTION = 0x00000021
114 | DIF_REGISTER_COINSTALLERS DI_FUNCTION = 0x00000022
115 | DIF_ADDPROPERTYPAGE_ADVANCED DI_FUNCTION = 0x00000023
116 | DIF_ADDPROPERTYPAGE_BASIC DI_FUNCTION = 0x00000024
117 | DIF_TROUBLESHOOTER DI_FUNCTION = 0x00000026
118 | DIF_POWERMESSAGEWAKE DI_FUNCTION = 0x00000027
119 | DIF_ADDREMOTEPROPERTYPAGE_ADVANCED DI_FUNCTION = 0x00000028
120 | DIF_UPDATEDRIVER_UI DI_FUNCTION = 0x00000029
121 | DIF_FINISHINSTALL_ACTION DI_FUNCTION = 0x0000002A
122 | )
123 |
124 | // DevInstallParams is device installation parameters structure (associated with a particular device information element, or globally with a device information set)
125 | type DevInstallParams struct {
126 | size uint32
127 | Flags DI_FLAGS
128 | FlagsEx DI_FLAGSEX
129 | hwndParent uintptr
130 | InstallMsgHandler uintptr
131 | InstallMsgHandlerContext uintptr
132 | FileQueue HSPFILEQ
133 | _ uintptr
134 | _ uint32
135 | driverPath [windows.MAX_PATH]uint16
136 | }
137 |
138 | func (params *DevInstallParams) GetDriverPath() string {
139 | return windows.UTF16ToString(params.driverPath[:])
140 | }
141 |
142 | func (params *DevInstallParams) SetDriverPath(driverPath string) error {
143 | str, err := syscall.UTF16FromString(driverPath)
144 | if err != nil {
145 | return err
146 | }
147 | copy(params.driverPath[:], str)
148 | return nil
149 | }
150 |
151 | // DI_FLAGS is SP_DEVINSTALL_PARAMS.Flags values
152 | type DI_FLAGS uint32
153 |
154 | const (
155 | // Flags for choosing a device
156 | DI_SHOWOEM DI_FLAGS = 0x00000001 // support Other... button
157 | DI_SHOWCOMPAT DI_FLAGS = 0x00000002 // show compatibility list
158 | DI_SHOWCLASS DI_FLAGS = 0x00000004 // show class list
159 | DI_SHOWALL DI_FLAGS = 0x00000007 // both class & compat list shown
160 | DI_NOVCP DI_FLAGS = 0x00000008 // don't create a new copy queue--use caller-supplied FileQueue
161 | DI_DIDCOMPAT DI_FLAGS = 0x00000010 // Searched for compatible devices
162 | DI_DIDCLASS DI_FLAGS = 0x00000020 // Searched for class devices
163 | DI_AUTOASSIGNRES DI_FLAGS = 0x00000040 // No UI for resources if possible
164 |
165 | // Flags returned by DiInstallDevice to indicate need to reboot/restart
166 | DI_NEEDRESTART DI_FLAGS = 0x00000080 // Reboot required to take effect
167 | DI_NEEDREBOOT DI_FLAGS = 0x00000100 // ""
168 |
169 | // Flags for device installation
170 | DI_NOBROWSE DI_FLAGS = 0x00000200 // no Browse... in InsertDisk
171 |
172 | // Flags set by DiBuildDriverInfoList
173 | DI_MULTMFGS DI_FLAGS = 0x00000400 // Set if multiple manufacturers in class driver list
174 |
175 | // Flag indicates that device is disabled
176 | DI_DISABLED DI_FLAGS = 0x00000800 // Set if device disabled
177 |
178 | // Flags for Device/Class Properties
179 | DI_GENERALPAGE_ADDED DI_FLAGS = 0x00001000
180 | DI_RESOURCEPAGE_ADDED DI_FLAGS = 0x00002000
181 |
182 | // Flag to indicate the setting properties for this Device (or class) caused a change so the Dev Mgr UI probably needs to be updated.
183 | DI_PROPERTIES_CHANGE DI_FLAGS = 0x00004000
184 |
185 | // Flag to indicate that the sorting from the INF file should be used.
186 | DI_INF_IS_SORTED DI_FLAGS = 0x00008000
187 |
188 | // Flag to indicate that only the the INF specified by SP_DEVINSTALL_PARAMS.DriverPath should be searched.
189 | DI_ENUMSINGLEINF DI_FLAGS = 0x00010000
190 |
191 | // Flag that prevents ConfigMgr from removing/re-enumerating devices during device
192 | // registration, installation, and deletion.
193 | DI_DONOTCALLCONFIGMG DI_FLAGS = 0x00020000
194 |
195 | // The following flag can be used to install a device disabled
196 | DI_INSTALLDISABLED DI_FLAGS = 0x00040000
197 |
198 | // Flag that causes SetupDiBuildDriverInfoList to build a device's compatible driver
199 | // list from its existing class driver list, instead of the normal INF search.
200 | DI_COMPAT_FROM_CLASS DI_FLAGS = 0x00080000
201 |
202 | // This flag is set if the Class Install params should be used.
203 | DI_CLASSINSTALLPARAMS DI_FLAGS = 0x00100000
204 |
205 | // This flag is set if the caller of DiCallClassInstaller does NOT want the internal default action performed if the Class installer returns ERROR_DI_DO_DEFAULT.
206 | DI_NODI_DEFAULTACTION DI_FLAGS = 0x00200000
207 |
208 | // Flags for device installation
209 | DI_QUIETINSTALL DI_FLAGS = 0x00800000 // don't confuse the user with questions or excess info
210 | DI_NOFILECOPY DI_FLAGS = 0x01000000 // No file Copy necessary
211 | DI_FORCECOPY DI_FLAGS = 0x02000000 // Force files to be copied from install path
212 | DI_DRIVERPAGE_ADDED DI_FLAGS = 0x04000000 // Prop provider added Driver page.
213 | DI_USECI_SELECTSTRINGS DI_FLAGS = 0x08000000 // Use Class Installer Provided strings in the Select Device Dlg
214 | DI_OVERRIDE_INFFLAGS DI_FLAGS = 0x10000000 // Override INF flags
215 | DI_PROPS_NOCHANGEUSAGE DI_FLAGS = 0x20000000 // No Enable/Disable in General Props
216 |
217 | DI_NOSELECTICONS DI_FLAGS = 0x40000000 // No small icons in select device dialogs
218 |
219 | DI_NOWRITE_IDS DI_FLAGS = 0x80000000 // Don't write HW & Compat IDs on install
220 | )
221 |
222 | // DI_FLAGSEX is SP_DEVINSTALL_PARAMS.FlagsEx values
223 | type DI_FLAGSEX uint32
224 |
225 | const (
226 | DI_FLAGSEX_CI_FAILED DI_FLAGSEX = 0x00000004 // Failed to Load/Call class installer
227 | DI_FLAGSEX_FINISHINSTALL_ACTION DI_FLAGSEX = 0x00000008 // Class/co-installer wants to get a DIF_FINISH_INSTALL action in client context.
228 | DI_FLAGSEX_DIDINFOLIST DI_FLAGSEX = 0x00000010 // Did the Class Info List
229 | DI_FLAGSEX_DIDCOMPATINFO DI_FLAGSEX = 0x00000020 // Did the Compat Info List
230 | DI_FLAGSEX_FILTERCLASSES DI_FLAGSEX = 0x00000040
231 | DI_FLAGSEX_SETFAILEDINSTALL DI_FLAGSEX = 0x00000080
232 | DI_FLAGSEX_DEVICECHANGE DI_FLAGSEX = 0x00000100
233 | DI_FLAGSEX_ALWAYSWRITEIDS DI_FLAGSEX = 0x00000200
234 | DI_FLAGSEX_PROPCHANGE_PENDING DI_FLAGSEX = 0x00000400 // One or more device property sheets have had changes made to them, and need to have a DIF_PROPERTYCHANGE occur.
235 | DI_FLAGSEX_ALLOWEXCLUDEDDRVS DI_FLAGSEX = 0x00000800
236 | DI_FLAGSEX_NOUIONQUERYREMOVE DI_FLAGSEX = 0x00001000
237 | DI_FLAGSEX_USECLASSFORCOMPAT DI_FLAGSEX = 0x00002000 // Use the device's class when building compat drv list. (Ignored if DI_COMPAT_FROM_CLASS flag is specified.)
238 | DI_FLAGSEX_NO_DRVREG_MODIFY DI_FLAGSEX = 0x00008000 // Don't run AddReg and DelReg for device's software (driver) key.
239 | DI_FLAGSEX_IN_SYSTEM_SETUP DI_FLAGSEX = 0x00010000 // Installation is occurring during initial system setup.
240 | DI_FLAGSEX_INET_DRIVER DI_FLAGSEX = 0x00020000 // Driver came from Windows Update
241 | DI_FLAGSEX_APPENDDRIVERLIST DI_FLAGSEX = 0x00040000 // Cause SetupDiBuildDriverInfoList to append a new driver list to an existing list.
242 | DI_FLAGSEX_PREINSTALLBACKUP DI_FLAGSEX = 0x00080000 // not used
243 | DI_FLAGSEX_BACKUPONREPLACE DI_FLAGSEX = 0x00100000 // not used
244 | DI_FLAGSEX_DRIVERLIST_FROM_URL DI_FLAGSEX = 0x00200000 // build driver list from INF(s) retrieved from URL specified in SP_DEVINSTALL_PARAMS.DriverPath (empty string means Windows Update website)
245 | DI_FLAGSEX_EXCLUDE_OLD_INET_DRIVERS DI_FLAGSEX = 0x00800000 // Don't include old Internet drivers when building a driver list. Ignored on Windows Vista and later.
246 | DI_FLAGSEX_POWERPAGE_ADDED DI_FLAGSEX = 0x01000000 // class installer added their own power page
247 | DI_FLAGSEX_FILTERSIMILARDRIVERS DI_FLAGSEX = 0x02000000 // only include similar drivers in class list
248 | DI_FLAGSEX_INSTALLEDDRIVER DI_FLAGSEX = 0x04000000 // only add the installed driver to the class or compat driver list. Used in calls to SetupDiBuildDriverInfoList
249 | DI_FLAGSEX_NO_CLASSLIST_NODE_MERGE DI_FLAGSEX = 0x08000000 // Don't remove identical driver nodes from the class list
250 | DI_FLAGSEX_ALTPLATFORM_DRVSEARCH DI_FLAGSEX = 0x10000000 // Build driver list based on alternate platform information specified in associated file queue
251 | DI_FLAGSEX_RESTART_DEVICE_ONLY DI_FLAGSEX = 0x20000000 // only restart the device drivers are being installed on as opposed to restarting all devices using those drivers.
252 | DI_FLAGSEX_RECURSIVESEARCH DI_FLAGSEX = 0x40000000 // Tell SetupDiBuildDriverInfoList to do a recursive search
253 | DI_FLAGSEX_SEARCH_PUBLISHED_INFS DI_FLAGSEX = 0x80000000 // Tell SetupDiBuildDriverInfoList to do a "published INF" search
254 | )
255 |
256 | // ClassInstallHeader is the first member of any class install parameters structure. It contains the device installation request code that defines the format of the rest of the install parameters structure.
257 | type ClassInstallHeader struct {
258 | size uint32
259 | InstallFunction DI_FUNCTION
260 | }
261 |
262 | func MakeClassInstallHeader(installFunction DI_FUNCTION) *ClassInstallHeader {
263 | hdr := &ClassInstallHeader{InstallFunction: installFunction}
264 | hdr.size = uint32(unsafe.Sizeof(*hdr))
265 | return hdr
266 | }
267 |
268 | // DICS_STATE specifies values indicating a change in a device's state
269 | type DICS_STATE uint32
270 |
271 | const (
272 | DICS_ENABLE DICS_STATE = 0x00000001 // The device is being enabled.
273 | DICS_DISABLE DICS_STATE = 0x00000002 // The device is being disabled.
274 | DICS_PROPCHANGE DICS_STATE = 0x00000003 // The properties of the device have changed.
275 | DICS_START DICS_STATE = 0x00000004 // The device is being started (if the request is for the currently active hardware profile).
276 | DICS_STOP DICS_STATE = 0x00000005 // The device is being stopped. The driver stack will be unloaded and the CSCONFIGFLAG_DO_NOT_START flag will be set for the device.
277 | )
278 |
279 | // DICS_FLAG specifies the scope of a device property change
280 | type DICS_FLAG uint32
281 |
282 | const (
283 | DICS_FLAG_GLOBAL DICS_FLAG = 0x00000001 // make change in all hardware profiles
284 | DICS_FLAG_CONFIGSPECIFIC DICS_FLAG = 0x00000002 // make change in specified profile only
285 | DICS_FLAG_CONFIGGENERAL DICS_FLAG = 0x00000004 // 1 or more hardware profile-specific changes to follow (obsolete)
286 | )
287 |
288 | // PropChangeParams is a structure corresponding to a DIF_PROPERTYCHANGE install function.
289 | type PropChangeParams struct {
290 | ClassInstallHeader ClassInstallHeader
291 | StateChange DICS_STATE
292 | Scope DICS_FLAG
293 | HwProfile uint32
294 | }
295 |
296 | // DI_REMOVEDEVICE specifies the scope of the device removal
297 | type DI_REMOVEDEVICE uint32
298 |
299 | const (
300 | DI_REMOVEDEVICE_GLOBAL DI_REMOVEDEVICE = 0x00000001 // Make this change in all hardware profiles. Remove information about the device from the registry.
301 | DI_REMOVEDEVICE_CONFIGSPECIFIC DI_REMOVEDEVICE = 0x00000002 // Make this change to only the hardware profile specified by HwProfile. this flag only applies to root-enumerated devices. When Windows removes the device from the last hardware profile in which it was configured, Windows performs a global removal.
302 | )
303 |
304 | // RemoveDeviceParams is a structure corresponding to a DIF_REMOVE install function.
305 | type RemoveDeviceParams struct {
306 | ClassInstallHeader ClassInstallHeader
307 | Scope DI_REMOVEDEVICE
308 | HwProfile uint32
309 | }
310 |
311 | // DrvInfoData is driver information structure (member of a driver info list that may be associated with a particular device instance, or (globally) with a device information set)
312 | type DrvInfoData struct {
313 | size uint32
314 | DriverType uint32
315 | _ uintptr
316 | description [LINE_LEN]uint16
317 | mfgName [LINE_LEN]uint16
318 | providerName [LINE_LEN]uint16
319 | DriverDate windows.Filetime
320 | DriverVersion uint64
321 | }
322 |
323 | func (data *DrvInfoData) GetDescription() string {
324 | return windows.UTF16ToString(data.description[:])
325 | }
326 |
327 | func (data *DrvInfoData) SetDescription(description string) error {
328 | str, err := syscall.UTF16FromString(description)
329 | if err != nil {
330 | return err
331 | }
332 | copy(data.description[:], str)
333 | return nil
334 | }
335 |
336 | func (data *DrvInfoData) GetMfgName() string {
337 | return windows.UTF16ToString(data.mfgName[:])
338 | }
339 |
340 | func (data *DrvInfoData) SetMfgName(mfgName string) error {
341 | str, err := syscall.UTF16FromString(mfgName)
342 | if err != nil {
343 | return err
344 | }
345 | copy(data.mfgName[:], str)
346 | return nil
347 | }
348 |
349 | func (data *DrvInfoData) GetProviderName() string {
350 | return windows.UTF16ToString(data.providerName[:])
351 | }
352 |
353 | func (data *DrvInfoData) SetProviderName(providerName string) error {
354 | str, err := syscall.UTF16FromString(providerName)
355 | if err != nil {
356 | return err
357 | }
358 | copy(data.providerName[:], str)
359 | return nil
360 | }
361 |
362 | // IsNewer method returns true if DrvInfoData date and version is newer than supplied parameters.
363 | func (data *DrvInfoData) IsNewer(driverDate windows.Filetime, driverVersion uint64) bool {
364 | if data.DriverDate.HighDateTime > driverDate.HighDateTime {
365 | return true
366 | }
367 | if data.DriverDate.HighDateTime < driverDate.HighDateTime {
368 | return false
369 | }
370 |
371 | if data.DriverDate.LowDateTime > driverDate.LowDateTime {
372 | return true
373 | }
374 | if data.DriverDate.LowDateTime < driverDate.LowDateTime {
375 | return false
376 | }
377 |
378 | if data.DriverVersion > driverVersion {
379 | return true
380 | }
381 | if data.DriverVersion < driverVersion {
382 | return false
383 | }
384 |
385 | return false
386 | }
387 |
388 | // DrvInfoDetailData is driver information details structure (provides detailed information about a particular driver information structure)
389 | type DrvInfoDetailData struct {
390 | size uint32 // On input, this must be exactly the sizeof(DrvInfoDetailData). On output, we set this member to the actual size of structure data.
391 | InfDate windows.Filetime
392 | compatIDsOffset uint32
393 | compatIDsLength uint32
394 | _ uintptr
395 | sectionName [LINE_LEN]uint16
396 | infFileName [windows.MAX_PATH]uint16
397 | drvDescription [LINE_LEN]uint16
398 | hardwareID [1]uint16
399 | }
400 |
401 | func (data *DrvInfoDetailData) GetSectionName() string {
402 | return windows.UTF16ToString(data.sectionName[:])
403 | }
404 |
405 | func (data *DrvInfoDetailData) GetInfFileName() string {
406 | return windows.UTF16ToString(data.infFileName[:])
407 | }
408 |
409 | func (data *DrvInfoDetailData) GetDrvDescription() string {
410 | return windows.UTF16ToString(data.drvDescription[:])
411 | }
412 |
413 | func (data *DrvInfoDetailData) GetHardwareID() string {
414 | if data.compatIDsOffset > 1 {
415 | bufW := data.getBuf()
416 | return windows.UTF16ToString(bufW[:wcslen(bufW)])
417 | }
418 |
419 | return ""
420 | }
421 |
422 | func (data *DrvInfoDetailData) GetCompatIDs() []string {
423 | a := make([]string, 0)
424 |
425 | if data.compatIDsLength > 0 {
426 | bufW := data.getBuf()
427 | bufW = bufW[data.compatIDsOffset : data.compatIDsOffset+data.compatIDsLength]
428 | for i := 0; i < len(bufW); {
429 | j := i + wcslen(bufW[i:])
430 | if i < j {
431 | a = append(a, windows.UTF16ToString(bufW[i:j]))
432 | }
433 | i = j + 1
434 | }
435 | }
436 |
437 | return a
438 | }
439 |
440 | func (data *DrvInfoDetailData) getBuf() []uint16 {
441 | length := (data.size - uint32(unsafe.Offsetof(data.hardwareID))) / 2
442 | sl := struct {
443 | addr *uint16
444 | len int
445 | cap int
446 | }{&data.hardwareID[0], int(length), int(length)}
447 | return *(*[]uint16)(unsafe.Pointer(&sl))
448 | }
449 |
450 | // IsCompatible method tests if given hardware ID matches the driver or is listed on the compatible ID list.
451 | func (data *DrvInfoDetailData) IsCompatible(hwid string) bool {
452 | hwidLC := strings.ToLower(hwid)
453 | if strings.EqualFold(data.GetHardwareID(), hwidLC) {
454 | return true
455 | }
456 | a := data.GetCompatIDs()
457 | for i := range a {
458 | if strings.EqualFold(a[i], hwidLC) {
459 | return true
460 | }
461 | }
462 |
463 | return false
464 | }
465 |
466 | // DICD flags control SetupDiCreateDeviceInfo
467 | type DICD uint32
468 |
469 | const (
470 | DICD_GENERATE_ID DICD = 0x00000001
471 | DICD_INHERIT_CLASSDRVS DICD = 0x00000002
472 | )
473 |
474 | // SPDIT flags to distinguish between class drivers and
475 | // device drivers.
476 | // (Passed in 'DriverType' parameter of driver information list APIs)
477 | type SPDIT uint32
478 |
479 | const (
480 | SPDIT_NODRIVER SPDIT = 0x00000000
481 | SPDIT_CLASSDRIVER SPDIT = 0x00000001
482 | SPDIT_COMPATDRIVER SPDIT = 0x00000002
483 | )
484 |
485 | // DIGCF flags control what is included in the device information set built by SetupDiGetClassDevs
486 | type DIGCF uint32
487 |
488 | const (
489 | DIGCF_DEFAULT DIGCF = 0x00000001 // only valid with DIGCF_DEVICEINTERFACE
490 | DIGCF_PRESENT DIGCF = 0x00000002
491 | DIGCF_ALLCLASSES DIGCF = 0x00000004
492 | DIGCF_PROFILE DIGCF = 0x00000008
493 | DIGCF_DEVICEINTERFACE DIGCF = 0x00000010
494 | )
495 |
496 | // DIREG specifies values for SetupDiCreateDevRegKey, SetupDiOpenDevRegKey, and SetupDiDeleteDevRegKey.
497 | type DIREG uint32
498 |
499 | const (
500 | DIREG_DEV DIREG = 0x00000001 // Open/Create/Delete device key
501 | DIREG_DRV DIREG = 0x00000002 // Open/Create/Delete driver key
502 | DIREG_BOTH DIREG = 0x00000004 // Delete both driver and Device key
503 | )
504 |
505 | // SPDRP specifies device registry property codes
506 | // (Codes marked as read-only (R) may only be used for
507 | // SetupDiGetDeviceRegistryProperty)
508 | //
509 | // These values should cover the same set of registry properties
510 | // as defined by the CM_DRP codes in cfgmgr32.h.
511 | //
512 | // Note that SPDRP codes are zero based while CM_DRP codes are one based!
513 | type SPDRP uint32
514 |
515 | const (
516 | SPDRP_DEVICEDESC SPDRP = 0x00000000 // DeviceDesc (R/W)
517 | SPDRP_HARDWAREID SPDRP = 0x00000001 // HardwareID (R/W)
518 | SPDRP_COMPATIBLEIDS SPDRP = 0x00000002 // CompatibleIDs (R/W)
519 | SPDRP_SERVICE SPDRP = 0x00000004 // Service (R/W)
520 | SPDRP_CLASS SPDRP = 0x00000007 // Class (R--tied to ClassGUID)
521 | SPDRP_CLASSGUID SPDRP = 0x00000008 // ClassGUID (R/W)
522 | SPDRP_DRIVER SPDRP = 0x00000009 // Driver (R/W)
523 | SPDRP_CONFIGFLAGS SPDRP = 0x0000000A // ConfigFlags (R/W)
524 | SPDRP_MFG SPDRP = 0x0000000B // Mfg (R/W)
525 | SPDRP_FRIENDLYNAME SPDRP = 0x0000000C // FriendlyName (R/W)
526 | SPDRP_LOCATION_INFORMATION SPDRP = 0x0000000D // LocationInformation (R/W)
527 | SPDRP_PHYSICAL_DEVICE_OBJECT_NAME SPDRP = 0x0000000E // PhysicalDeviceObjectName (R)
528 | SPDRP_CAPABILITIES SPDRP = 0x0000000F // Capabilities (R)
529 | SPDRP_UI_NUMBER SPDRP = 0x00000010 // UiNumber (R)
530 | SPDRP_UPPERFILTERS SPDRP = 0x00000011 // UpperFilters (R/W)
531 | SPDRP_LOWERFILTERS SPDRP = 0x00000012 // LowerFilters (R/W)
532 | SPDRP_BUSTYPEGUID SPDRP = 0x00000013 // BusTypeGUID (R)
533 | SPDRP_LEGACYBUSTYPE SPDRP = 0x00000014 // LegacyBusType (R)
534 | SPDRP_BUSNUMBER SPDRP = 0x00000015 // BusNumber (R)
535 | SPDRP_ENUMERATOR_NAME SPDRP = 0x00000016 // Enumerator Name (R)
536 | SPDRP_SECURITY SPDRP = 0x00000017 // Security (R/W, binary form)
537 | SPDRP_SECURITY_SDS SPDRP = 0x00000018 // Security (W, SDS form)
538 | SPDRP_DEVTYPE SPDRP = 0x00000019 // Device Type (R/W)
539 | SPDRP_EXCLUSIVE SPDRP = 0x0000001A // Device is exclusive-access (R/W)
540 | SPDRP_CHARACTERISTICS SPDRP = 0x0000001B // Device Characteristics (R/W)
541 | SPDRP_ADDRESS SPDRP = 0x0000001C // Device Address (R)
542 | SPDRP_UI_NUMBER_DESC_FORMAT SPDRP = 0x0000001D // UiNumberDescFormat (R/W)
543 | SPDRP_DEVICE_POWER_DATA SPDRP = 0x0000001E // Device Power Data (R)
544 | SPDRP_REMOVAL_POLICY SPDRP = 0x0000001F // Removal Policy (R)
545 | SPDRP_REMOVAL_POLICY_HW_DEFAULT SPDRP = 0x00000020 // Hardware Removal Policy (R)
546 | SPDRP_REMOVAL_POLICY_OVERRIDE SPDRP = 0x00000021 // Removal Policy Override (RW)
547 | SPDRP_INSTALL_STATE SPDRP = 0x00000022 // Device Install State (R)
548 | SPDRP_LOCATION_PATHS SPDRP = 0x00000023 // Device Location Paths (R)
549 | SPDRP_BASE_CONTAINERID SPDRP = 0x00000024 // Base ContainerID (R)
550 |
551 | SPDRP_MAXIMUM_PROPERTY SPDRP = 0x00000025 // Upper bound on ordinals
552 | )
553 |
554 | type DEVPROPKEY struct {
555 | fmtid windows.GUID
556 | pid uint32
557 | }
558 |
559 | //
560 | // This property describes the hardware support for interrupts on the device.
561 | // It is a bitmask of supported interrupt types, with the following values:
562 | //
563 |
564 | // #define DevProp_PciDevice_InterruptType_LineBased 1
565 | // #define DevProp_PciDevice_InterruptType_Msi 2
566 | // #define DevProp_PciDevice_InterruptType_MsiX 4
567 |
568 | var DEVPKEY_PciDevice_InterruptSupport = DEVPROPKEY{
569 | windows.GUID{Data1: 0x3ab22e31, Data2: 0x8264, Data3: 0x4b4e, Data4: [8]byte{0x9a, 0xf5, 0xa8, 0xd2, 0xd8, 0xe3, 0x3e, 0x62}},
570 | 14,
571 | }
572 |
573 | //
574 | // This property describes the number of message interrupts a device supports
575 | // in hardware. This property is only valid if the device supports message
576 | // interrupts.
577 | //
578 |
579 | var DEVPKEY_PciDevice_InterruptMessageMaximum = DEVPROPKEY{
580 | windows.GUID{Data1: 0x3ab22e31, Data2: 0x8264, Data3: 0x4b4e, Data4: [8]byte{0x9a, 0xf5, 0xa8, 0xd2, 0xd8, 0xe3, 0x3e, 0x62}},
581 | 15,
582 | }
583 |
--------------------------------------------------------------------------------
/winapi.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "syscall"
5 | "unsafe"
6 |
7 | "golang.org/x/sys/windows"
8 | "golang.org/x/sys/windows/registry"
9 | )
10 |
11 | var (
12 | modSetupapi = windows.NewLazyDLL("setupapi.dll")
13 |
14 | procSetupDiGetClassDevsW = modSetupapi.NewProc("SetupDiGetClassDevsW")
15 | )
16 |
17 | const CONFIG_FLAG_DISABLED uint32 = 1
18 |
19 | func FindAllDevices() ([]Device, DevInfo) {
20 | var allDevices []Device
21 | handle, err := SetupDiGetClassDevs(nil, nil, 0, uint32(DIGCF_ALLCLASSES|DIGCF_PRESENT))
22 | if err != nil {
23 | panic(err)
24 | }
25 |
26 | var index = 0
27 | for {
28 | idata, err := SetupDiEnumDeviceInfo(handle, index)
29 | if err != nil { // ERROR_NO_MORE_ITEMS
30 | break
31 | }
32 | index++
33 |
34 | dev := Device{
35 | Idata: *idata,
36 | }
37 |
38 | val, err := SetupDiGetDeviceRegistryProperty(handle, idata, SPDRP_CONFIGFLAGS)
39 | if err == nil {
40 | if val.(uint32)&CONFIG_FLAG_DISABLED != 0 {
41 | // Sorts out deactivated devices
42 | continue
43 | }
44 | }
45 |
46 | val, err = SetupDiGetDeviceRegistryProperty(handle, idata, SPDRP_DEVICEDESC)
47 | if err == nil {
48 | if val.(string) == "" {
49 | continue
50 | }
51 | dev.DeviceDesc = val.(string)
52 | } else {
53 | continue
54 | }
55 |
56 | valProp, err := GetDeviceProperty(handle, idata, DEVPKEY_PciDevice_InterruptSupport)
57 | if err == nil {
58 | dev.InterruptTypeMap = Bits(btoi16(valProp))
59 | }
60 |
61 | valProp, err = GetDeviceProperty(handle, idata, DEVPKEY_PciDevice_InterruptMessageMaximum)
62 | if err == nil {
63 | dev.MaxMSILimit = btoi32(valProp)
64 | }
65 |
66 | val, err = SetupDiGetDeviceRegistryProperty(handle, idata, SPDRP_FRIENDLYNAME)
67 | if err == nil {
68 | dev.FriendlyName = val.(string)
69 | }
70 |
71 | val, err = SetupDiGetDeviceRegistryProperty(handle, idata, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME)
72 | if err == nil {
73 | dev.DevObjName = val.(string)
74 | }
75 |
76 | val, err = SetupDiGetDeviceRegistryProperty(handle, idata, SPDRP_LOCATION_INFORMATION)
77 | if err == nil {
78 | dev.LocationInformation = val.(string)
79 | }
80 |
81 | dev.reg, _ = SetupDiOpenDevRegKey(handle, idata, DICS_FLAG_GLOBAL, 0, DIREG_DEV, windows.KEY_SET_VALUE)
82 |
83 | keyinfo, err := dev.reg.Stat()
84 | if err == nil {
85 | dev.LastChange = keyinfo.ModTime()
86 | }
87 |
88 | affinityPolicyKey, _ := registry.OpenKey(dev.reg, `Interrupt Management\Affinity Policy`, registry.QUERY_VALUE)
89 | dev.DevicePolicy = GetDWORDuint32Value(affinityPolicyKey, "DevicePolicy") // REG_DWORD
90 | dev.DevicePriority = GetDWORDuint32Value(affinityPolicyKey, "DevicePriority") // REG_DWORD
91 | AssignmentSetOverrideByte := GetBinaryValue(affinityPolicyKey, "AssignmentSetOverride") // REG_BINARY
92 | affinityPolicyKey.Close()
93 |
94 | if len(AssignmentSetOverrideByte) != 0 {
95 | AssignmentSetOverrideBytes := make([]byte, 8)
96 | copy(AssignmentSetOverrideBytes, AssignmentSetOverrideByte)
97 | dev.AssignmentSetOverride = Bits(btoi64(AssignmentSetOverrideBytes))
98 | }
99 |
100 | if dev.InterruptTypeMap != ZeroBit {
101 | messageSignaledInterruptPropertiesKey, _ := registry.OpenKey(dev.reg, `Interrupt Management\MessageSignaledInterruptProperties`, registry.QUERY_VALUE)
102 | dev.MessageNumberLimit = GetDWORDuint32Value(messageSignaledInterruptPropertiesKey, "MessageNumberLimit") // REG_DWORD https://docs.microsoft.com/de-de/windows-hardware/drivers/kernel/enabling-message-signaled-interrupts-in-the-registry
103 | dev.MsiSupported = GetDWORDuint32Value(messageSignaledInterruptPropertiesKey, "MSISupported") // REG_DWORD
104 | messageSignaledInterruptPropertiesKey.Close()
105 | } else {
106 | dev.MsiSupported = 2 // invalid
107 | }
108 |
109 | allDevices = append(allDevices, dev)
110 | }
111 | return allDevices, handle
112 | }
113 |
114 | func SetupDiGetClassDevs(classGuid *windows.GUID, enumerator *uint16, hwndParent uintptr, flags uint32) (handle DevInfo, err error) {
115 | r0, _, e1 := syscall.SyscallN(procSetupDiGetClassDevsW.Addr(), uintptr(unsafe.Pointer(classGuid)), uintptr(unsafe.Pointer(enumerator)), uintptr(hwndParent), uintptr(flags))
116 | handle = DevInfo(r0)
117 | if handle == DevInfo(windows.InvalidHandle) {
118 | if e1 != 0 {
119 | err = error(e1)
120 | } else {
121 | err = syscall.EINVAL
122 | }
123 | }
124 | return
125 | }
126 |
127 | func GetDeviceProperty(dis DevInfo, devInfoData *DevInfoData, devPropKey DEVPROPKEY) ([]byte, error) {
128 | var propt, size uint32
129 | buf := make([]byte, 16)
130 | run := true
131 | for run {
132 | err := SetupDiGetDeviceProperty(dis, devInfoData, &devPropKey, &propt, &buf[0], uint32(len(buf)), &size, 0)
133 | switch {
134 | case size > uint32(len(buf)):
135 | buf = make([]byte, size+16)
136 | case err != nil:
137 | return buf, err
138 | default:
139 | run = false
140 | }
141 | }
142 |
143 | return buf, nil
144 | }
145 |
--------------------------------------------------------------------------------
/zsetupapi_windows.go:
--------------------------------------------------------------------------------
1 | // Code generated by 'go generate'; DO NOT EDIT.
2 |
3 | package main
4 |
5 | import (
6 | "syscall"
7 | "unsafe"
8 |
9 | "golang.org/x/sys/windows"
10 | )
11 |
12 | var _ unsafe.Pointer
13 |
14 | // Do the interface allocations only once for common
15 | // Errno values.
16 | const (
17 | errnoERROR_IO_PENDING = 997
18 | )
19 |
20 | var (
21 | errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING)
22 | )
23 |
24 | // errnoErr returns common boxed Errno values, to prevent
25 | // allocations at runtime.
26 | func errnoErr(e syscall.Errno) error {
27 | switch e {
28 | case 0:
29 | return nil
30 | case errnoERROR_IO_PENDING:
31 | return errERROR_IO_PENDING
32 | }
33 | // TODO: add more here, after collecting data on the common
34 | // error values see on Windows. (perhaps when running
35 | // all.bat?)
36 | return e
37 | }
38 |
39 | var (
40 | modsetupapi = windows.NewLazySystemDLL("setupapi.dll")
41 |
42 | procSetupDiCreateDeviceInfoListExW = modsetupapi.NewProc("SetupDiCreateDeviceInfoListExW")
43 | procSetupDiGetDeviceInfoListDetailW = modsetupapi.NewProc("SetupDiGetDeviceInfoListDetailW")
44 | procSetupDiCreateDeviceInfoW = modsetupapi.NewProc("SetupDiCreateDeviceInfoW")
45 | procSetupDiEnumDeviceInfo = modsetupapi.NewProc("SetupDiEnumDeviceInfo")
46 | procSetupDiDestroyDeviceInfoList = modsetupapi.NewProc("SetupDiDestroyDeviceInfoList")
47 | procSetupDiBuildDriverInfoList = modsetupapi.NewProc("SetupDiBuildDriverInfoList")
48 | procSetupDiCancelDriverInfoSearch = modsetupapi.NewProc("SetupDiCancelDriverInfoSearch")
49 | procSetupDiEnumDriverInfoW = modsetupapi.NewProc("SetupDiEnumDriverInfoW")
50 | procSetupDiGetSelectedDriverW = modsetupapi.NewProc("SetupDiGetSelectedDriverW")
51 | procSetupDiSetSelectedDriverW = modsetupapi.NewProc("SetupDiSetSelectedDriverW")
52 | procSetupDiGetDriverInfoDetailW = modsetupapi.NewProc("SetupDiGetDriverInfoDetailW")
53 | procSetupDiDestroyDriverInfoList = modsetupapi.NewProc("SetupDiDestroyDriverInfoList")
54 | procSetupDiGetClassDevsExW = modsetupapi.NewProc("SetupDiGetClassDevsExW")
55 | procSetupDiCallClassInstaller = modsetupapi.NewProc("SetupDiCallClassInstaller")
56 | procSetupDiOpenDevRegKey = modsetupapi.NewProc("SetupDiOpenDevRegKey")
57 | procSetupDiGetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiGetDeviceRegistryPropertyW")
58 | procSetupDiSetDeviceRegistryPropertyW = modsetupapi.NewProc("SetupDiSetDeviceRegistryPropertyW")
59 | procSetupDiGetDeviceInstallParamsW = modsetupapi.NewProc("SetupDiGetDeviceInstallParamsW")
60 | procSetupDiGetClassInstallParamsW = modsetupapi.NewProc("SetupDiGetClassInstallParamsW")
61 | procSetupDiSetDeviceInstallParamsW = modsetupapi.NewProc("SetupDiSetDeviceInstallParamsW")
62 | procSetupDiSetClassInstallParamsW = modsetupapi.NewProc("SetupDiSetClassInstallParamsW")
63 | procSetupDiClassNameFromGuidExW = modsetupapi.NewProc("SetupDiClassNameFromGuidExW")
64 | procSetupDiClassGuidsFromNameExW = modsetupapi.NewProc("SetupDiClassGuidsFromNameExW")
65 | procSetupDiGetSelectedDevice = modsetupapi.NewProc("SetupDiGetSelectedDevice")
66 | procSetupDiSetSelectedDevice = modsetupapi.NewProc("SetupDiSetSelectedDevice")
67 | procSetupDiGetDevicePropertyW = modsetupapi.NewProc("SetupDiGetDevicePropertyW")
68 | procSetupDiRestartDevices = modsetupapi.NewProc("SetupDiRestartDevices")
69 | )
70 |
71 | func setupDiCreateDeviceInfoListEx(classGUID *windows.GUID, hwndParent uintptr, machineName *uint16, reserved uintptr) (handle DevInfo, err error) {
72 | r0, _, e1 := syscall.SyscallN(procSetupDiCreateDeviceInfoListExW.Addr(), uintptr(unsafe.Pointer(classGUID)), uintptr(hwndParent), uintptr(unsafe.Pointer(machineName)), uintptr(reserved))
73 | handle = DevInfo(r0)
74 | if handle == DevInfo(windows.InvalidHandle) {
75 | if e1 != 0 {
76 | err = errnoErr(e1)
77 | } else {
78 | err = syscall.EINVAL
79 | }
80 | }
81 | return
82 | }
83 |
84 | func setupDiGetDeviceInfoListDetail(deviceInfoSet DevInfo, deviceInfoSetDetailData *DevInfoListDetailData) (err error) {
85 | r1, _, e1 := syscall.SyscallN(procSetupDiGetDeviceInfoListDetailW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoSetDetailData)))
86 | if r1 == 0 {
87 | if e1 != 0 {
88 | err = errnoErr(e1)
89 | } else {
90 | err = syscall.EINVAL
91 | }
92 | }
93 | return
94 | }
95 |
96 | func setupDiCreateDeviceInfo(deviceInfoSet DevInfo, DeviceName *uint16, classGUID *windows.GUID, DeviceDescription *uint16, hwndParent uintptr, CreationFlags DICD, deviceInfoData *DevInfoData) (err error) {
97 | r1, _, e1 := syscall.SyscallN(procSetupDiCreateDeviceInfoW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(DeviceName)), uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(DeviceDescription)), uintptr(hwndParent), uintptr(CreationFlags), uintptr(unsafe.Pointer(deviceInfoData)))
98 | if r1 == 0 {
99 | if e1 != 0 {
100 | err = errnoErr(e1)
101 | } else {
102 | err = syscall.EINVAL
103 | }
104 | }
105 | return
106 | }
107 |
108 | func setupDiEnumDeviceInfo(deviceInfoSet DevInfo, memberIndex uint32, deviceInfoData *DevInfoData) (err error) {
109 | r1, _, e1 := syscall.SyscallN(procSetupDiEnumDeviceInfo.Addr(), uintptr(deviceInfoSet), uintptr(memberIndex), uintptr(unsafe.Pointer(deviceInfoData)))
110 | if r1 == 0 {
111 | if e1 != 0 {
112 | err = errnoErr(e1)
113 | } else {
114 | err = syscall.EINVAL
115 | }
116 | }
117 | return
118 | }
119 |
120 | func SetupDiDestroyDeviceInfoList(deviceInfoSet DevInfo) (err error) {
121 | r1, _, e1 := syscall.SyscallN(procSetupDiDestroyDeviceInfoList.Addr(), uintptr(deviceInfoSet))
122 | if r1 == 0 {
123 | if e1 != 0 {
124 | err = errnoErr(e1)
125 | } else {
126 | err = syscall.EINVAL
127 | }
128 | }
129 | return
130 | }
131 |
132 | func SetupDiBuildDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) {
133 | r1, _, e1 := syscall.SyscallN(procSetupDiBuildDriverInfoList.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType))
134 | if r1 == 0 {
135 | if e1 != 0 {
136 | err = errnoErr(e1)
137 | } else {
138 | err = syscall.EINVAL
139 | }
140 | }
141 | return
142 | }
143 |
144 | func SetupDiCancelDriverInfoSearch(deviceInfoSet DevInfo) (err error) {
145 | r1, _, e1 := syscall.SyscallN(procSetupDiCancelDriverInfoSearch.Addr(), uintptr(deviceInfoSet))
146 | if r1 == 0 {
147 | if e1 != 0 {
148 | err = errnoErr(e1)
149 | } else {
150 | err = syscall.EINVAL
151 | }
152 | }
153 | return
154 | }
155 |
156 | func setupDiEnumDriverInfo(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT, memberIndex uint32, driverInfoData *DrvInfoData) (err error) {
157 | r1, _, e1 := syscall.SyscallN(procSetupDiEnumDriverInfoW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType), uintptr(memberIndex), uintptr(unsafe.Pointer(driverInfoData)))
158 | if r1 == 0 {
159 | if e1 != 0 {
160 | err = errnoErr(e1)
161 | } else {
162 | err = syscall.EINVAL
163 | }
164 | }
165 | return
166 | }
167 |
168 | func setupDiGetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) {
169 | r1, _, e1 := syscall.SyscallN(procSetupDiGetSelectedDriverW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)))
170 | if r1 == 0 {
171 | if e1 != 0 {
172 | err = errnoErr(e1)
173 | } else {
174 | err = syscall.EINVAL
175 | }
176 | }
177 | return
178 | }
179 |
180 | func SetupDiSetSelectedDriver(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData) (err error) {
181 | r1, _, e1 := syscall.SyscallN(procSetupDiSetSelectedDriverW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)))
182 | if r1 == 0 {
183 | if e1 != 0 {
184 | err = errnoErr(e1)
185 | } else {
186 | err = syscall.EINVAL
187 | }
188 | }
189 | return
190 | }
191 |
192 | func setupDiGetDriverInfoDetail(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverInfoData *DrvInfoData, driverInfoDetailData *DrvInfoDetailData, driverInfoDetailDataSize uint32, requiredSize *uint32) (err error) {
193 | r1, _, e1 := syscall.SyscallN(procSetupDiGetDriverInfoDetailW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(driverInfoData)), uintptr(unsafe.Pointer(driverInfoDetailData)), uintptr(driverInfoDetailDataSize), uintptr(unsafe.Pointer(requiredSize)))
194 | if r1 == 0 {
195 | if e1 != 0 {
196 | err = errnoErr(e1)
197 | } else {
198 | err = syscall.EINVAL
199 | }
200 | }
201 | return
202 | }
203 |
204 | func SetupDiDestroyDriverInfoList(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, driverType SPDIT) (err error) {
205 | r1, _, e1 := syscall.SyscallN(procSetupDiDestroyDriverInfoList.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(driverType))
206 | if r1 == 0 {
207 | if e1 != 0 {
208 | err = errnoErr(e1)
209 | } else {
210 | err = syscall.EINVAL
211 | }
212 | }
213 | return
214 | }
215 |
216 | func setupDiGetClassDevsEx(classGUID *windows.GUID, Enumerator *uint16, hwndParent uintptr, Flags DIGCF, deviceInfoSet DevInfo, machineName *uint16, reserved uintptr) (handle DevInfo, err error) {
217 | r0, _, e1 := syscall.SyscallN(procSetupDiGetClassDevsExW.Addr(), uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(Enumerator)), uintptr(hwndParent), uintptr(Flags), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(machineName)), uintptr(reserved))
218 | handle = DevInfo(r0)
219 | if handle == DevInfo(windows.InvalidHandle) {
220 | if e1 != 0 {
221 | err = errnoErr(e1)
222 | } else {
223 | err = syscall.EINVAL
224 | }
225 | }
226 | return
227 | }
228 |
229 | func SetupDiCallClassInstaller(installFunction DI_FUNCTION, deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) {
230 | r1, _, e1 := syscall.SyscallN(
231 | procSetupDiCallClassInstaller.Addr(),
232 | uintptr(installFunction),
233 | uintptr(deviceInfoSet),
234 | uintptr(unsafe.Pointer(deviceInfoData)),
235 | )
236 | if r1 == 0 {
237 | if e1 != 0 {
238 | err = errnoErr(e1)
239 | } else {
240 | err = syscall.EINVAL
241 | }
242 | }
243 | return
244 | }
245 |
246 | func setupDiOpenDevRegKey(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, Scope DICS_FLAG, HwProfile uint32, KeyType DIREG, samDesired uint32) (key windows.Handle, err error) {
247 | r0, _, e1 := syscall.SyscallN(procSetupDiOpenDevRegKey.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(Scope), uintptr(HwProfile), uintptr(KeyType), uintptr(samDesired))
248 | key = windows.Handle(r0)
249 | if key == windows.InvalidHandle {
250 | if e1 != 0 {
251 | err = errnoErr(e1)
252 | } else {
253 | err = syscall.EINVAL
254 | }
255 | }
256 | return
257 | }
258 |
259 | func setupDiGetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyRegDataType *uint32, propertyBuffer *byte, propertyBufferSize uint32, requiredSize *uint32) (err error) {
260 | r1, _, e1 := syscall.SyscallN(procSetupDiGetDeviceRegistryPropertyW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(property), uintptr(unsafe.Pointer(propertyRegDataType)), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(propertyBufferSize), uintptr(unsafe.Pointer(requiredSize)))
261 | if r1 == 0 {
262 | if e1 != 0 {
263 | err = errnoErr(e1)
264 | } else {
265 | err = syscall.EINVAL
266 | }
267 | }
268 | return
269 | }
270 |
271 | func setupDiSetDeviceRegistryProperty(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, property SPDRP, propertyBuffer *byte, propertyBufferSize uint32) (err error) {
272 | r1, _, e1 := syscall.SyscallN(procSetupDiSetDeviceRegistryPropertyW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(property), uintptr(unsafe.Pointer(propertyBuffer)), uintptr(propertyBufferSize))
273 | if r1 == 0 {
274 | if e1 != 0 {
275 | err = errnoErr(e1)
276 | } else {
277 | err = syscall.EINVAL
278 | }
279 | }
280 | return
281 | }
282 |
283 | func setupDiGetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) {
284 | r1, _, e1 := syscall.SyscallN(procSetupDiGetDeviceInstallParamsW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(deviceInstallParams)))
285 | if r1 == 0 {
286 | if e1 != 0 {
287 | err = errnoErr(e1)
288 | } else {
289 | err = syscall.EINVAL
290 | }
291 | }
292 | return
293 | }
294 |
295 | func SetupDiGetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32, requiredSize *uint32) (err error) {
296 | r1, _, e1 := syscall.SyscallN(procSetupDiGetClassInstallParamsW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(classInstallParams)), uintptr(classInstallParamsSize), uintptr(unsafe.Pointer(requiredSize)))
297 | if r1 == 0 {
298 | if e1 != 0 {
299 | err = errnoErr(e1)
300 | } else {
301 | err = syscall.EINVAL
302 | }
303 | }
304 | return
305 | }
306 |
307 | func SetupDiSetDeviceInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, deviceInstallParams *DevInstallParams) (err error) {
308 | r1, _, e1 := syscall.SyscallN(procSetupDiSetDeviceInstallParamsW.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)), uintptr(unsafe.Pointer(deviceInstallParams)))
309 | if r1 == 0 {
310 | if e1 != 0 {
311 | err = errnoErr(e1)
312 | } else {
313 | err = syscall.EINVAL
314 | }
315 | }
316 | return
317 | }
318 |
319 | // WINSETUPAPI BOOL SetupDiRestartDevices(
320 | // [in] HDEVINFO DeviceInfoSet,
321 | // [in, out] PSP_DEVINFO_DATA DeviceInfoData
322 | // );
323 | func SetupDiRestartDevices(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) {
324 | r1, _, e1 := syscall.SyscallN(
325 | procSetupDiRestartDevices.Addr(),
326 | uintptr(deviceInfoSet),
327 | uintptr(unsafe.Pointer(deviceInfoData)),
328 | )
329 | if r1 == 0 {
330 | if e1 != 0 {
331 | err = errnoErr(e1)
332 | } else {
333 | err = syscall.EINVAL
334 | }
335 | }
336 | return
337 | }
338 |
339 | func SetupDiSetClassInstallParams(deviceInfoSet DevInfo, deviceInfoData *DevInfoData, classInstallParams *ClassInstallHeader, classInstallParamsSize uint32) (err error) {
340 | r1, _, e1 := syscall.SyscallN(procSetupDiSetClassInstallParamsW.Addr(),
341 | uintptr(deviceInfoSet),
342 | uintptr(unsafe.Pointer(deviceInfoData)),
343 | uintptr(unsafe.Pointer(classInstallParams)),
344 | uintptr(classInstallParamsSize),
345 | )
346 | if r1 == 0 {
347 | if e1 != 0 {
348 | err = errnoErr(e1)
349 | } else {
350 | err = syscall.EINVAL
351 | }
352 | }
353 | return
354 | }
355 |
356 | func setupDiClassNameFromGuidEx(classGUID *windows.GUID, className *uint16, classNameSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) {
357 | r1, _, e1 := syscall.SyscallN(procSetupDiClassNameFromGuidExW.Addr(), uintptr(unsafe.Pointer(classGUID)), uintptr(unsafe.Pointer(className)), uintptr(classNameSize), uintptr(unsafe.Pointer(requiredSize)), uintptr(unsafe.Pointer(machineName)), uintptr(reserved))
358 | if r1 == 0 {
359 | if e1 != 0 {
360 | err = errnoErr(e1)
361 | } else {
362 | err = syscall.EINVAL
363 | }
364 | }
365 | return
366 | }
367 |
368 | func setupDiClassGuidsFromNameEx(className *uint16, classGuidList *windows.GUID, classGuidListSize uint32, requiredSize *uint32, machineName *uint16, reserved uintptr) (err error) {
369 | r1, _, e1 := syscall.SyscallN(procSetupDiClassGuidsFromNameExW.Addr(), uintptr(unsafe.Pointer(className)), uintptr(unsafe.Pointer(classGuidList)), uintptr(classGuidListSize), uintptr(unsafe.Pointer(requiredSize)), uintptr(unsafe.Pointer(machineName)), uintptr(reserved))
370 | if r1 == 0 {
371 | if e1 != 0 {
372 | err = errnoErr(e1)
373 | } else {
374 | err = syscall.EINVAL
375 | }
376 | }
377 | return
378 | }
379 |
380 | func setupDiGetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) {
381 | r1, _, e1 := syscall.SyscallN(procSetupDiGetSelectedDevice.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)))
382 | if r1 == 0 {
383 | if e1 != 0 {
384 | err = errnoErr(e1)
385 | } else {
386 | err = syscall.EINVAL
387 | }
388 | }
389 | return
390 | }
391 |
392 | func SetupDiSetSelectedDevice(deviceInfoSet DevInfo, deviceInfoData *DevInfoData) (err error) {
393 | r1, _, e1 := syscall.SyscallN(procSetupDiSetSelectedDevice.Addr(), uintptr(deviceInfoSet), uintptr(unsafe.Pointer(deviceInfoData)))
394 | if r1 == 0 {
395 | if e1 != 0 {
396 | err = errnoErr(e1)
397 | } else {
398 | err = syscall.EINVAL
399 | }
400 | }
401 | return
402 | }
403 |
404 | // https://github.com/tajtiattila/hid/blob/2bd63ffd4c8c0b81e5999c7b183cc4325f773527/platform/zsys_windows.go
405 | func SetupDiGetDeviceProperty(devInfoSet DevInfo, devInfoData *DevInfoData, propKey *DEVPROPKEY, propType *uint32, propBuf *byte, propBufSize uint32, reqsize *uint32, flags uint32) (err error) {
406 | r1, _, e1 := syscall.SyscallN(procSetupDiGetDevicePropertyW.Addr(), uintptr(devInfoSet), uintptr(unsafe.Pointer(devInfoData)), uintptr(unsafe.Pointer(propKey)), uintptr(unsafe.Pointer(propType)), uintptr(unsafe.Pointer(propBuf)), uintptr(propBufSize), uintptr(unsafe.Pointer(reqsize)), uintptr(flags))
407 | if r1 == 0 {
408 | if e1 != 0 {
409 | err = error(e1)
410 | } else {
411 | err = syscall.EINVAL
412 | }
413 | }
414 | return
415 | }
416 |
--------------------------------------------------------------------------------