├── .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 | ![screenshot](https://github.com/user-attachments/assets/61818056-4b6c-4ee7-84cb-4226f3660ca3) 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 | --------------------------------------------------------------------------------