Initial commit: Driver Booster Pro - Go + WebView2 desktop app

Windows system utility with driver scanning, Windows Update integration,
and system info collection. Beautiful dark-themed UI via embedded WebView2.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
taqin
2026-04-12 20:42:34 +07:00
commit 3e432fbb50
11 changed files with 1638 additions and 0 deletions

59
internal/bridge/bridge.go Normal file
View File

@@ -0,0 +1,59 @@
package bridge
import (
"encoding/json"
"net/http"
"github.com/mumur/driver-booster/internal/drivers"
"github.com/mumur/driver-booster/internal/sysinfo"
"github.com/mumur/driver-booster/internal/winupdate"
)
type Bridge struct {
DriverScanner *drivers.Scanner
UpdateChecker *winupdate.Checker
SysInfo *sysinfo.Collector
}
func New(ds *drivers.Scanner, uc *winupdate.Checker, si *sysinfo.Collector) *Bridge {
return &Bridge{
DriverScanner: ds,
UpdateChecker: uc,
SysInfo: si,
}
}
func jsonResponse(w http.ResponseWriter, data interface{}) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(data)
}
func (b *Bridge) HandleSysInfo(w http.ResponseWriter, r *http.Request) {
info := b.SysInfo.Collect()
jsonResponse(w, info)
}
func (b *Bridge) HandleDrivers(w http.ResponseWriter, r *http.Request) {
result := b.DriverScanner.Scan()
jsonResponse(w, result)
}
func (b *Bridge) HandleDriverScan(w http.ResponseWriter, r *http.Request) {
result := b.DriverScanner.Scan()
jsonResponse(w, result)
}
func (b *Bridge) HandleUpdates(w http.ResponseWriter, r *http.Request) {
result := b.UpdateChecker.Check()
jsonResponse(w, result)
}
func (b *Bridge) HandleUpdateCheck(w http.ResponseWriter, r *http.Request) {
result := b.UpdateChecker.Check()
jsonResponse(w, result)
}
func (b *Bridge) HandleUpdateInstall(w http.ResponseWriter, r *http.Request) {
result := b.UpdateChecker.Install(nil)
jsonResponse(w, result)
}

153
internal/drivers/drivers.go Normal file
View File

@@ -0,0 +1,153 @@
package drivers
import (
"encoding/json"
"os/exec"
"strings"
"time"
)
type Driver struct {
DeviceName string `json:"deviceName"`
DeviceClass string `json:"deviceClass"`
Manufacturer string `json:"manufacturer"`
DriverVersion string `json:"driverVersion"`
DriverDate string `json:"driverDate"`
Status string `json:"status"`
InfName string `json:"infName"`
IsSigned bool `json:"isSigned"`
NeedsUpdate bool `json:"needsUpdate"`
}
type ScanResult struct {
Drivers []Driver `json:"drivers"`
TotalCount int `json:"totalCount"`
OutdatedCount int `json:"outdatedCount"`
ErrorCount int `json:"errorCount"`
ScanTime string `json:"scanTime"`
ScannedAt time.Time `json:"scannedAt"`
}
type Scanner struct{}
func NewScanner() *Scanner {
return &Scanner{}
}
func (s *Scanner) Scan() ScanResult {
start := time.Now()
result := ScanResult{
Drivers: []Driver{},
}
drivers := s.enumDriversPnP()
result.Drivers = drivers
result.TotalCount = len(drivers)
for _, d := range drivers {
if d.NeedsUpdate {
result.OutdatedCount++
}
if d.Status == "Error" || d.Status == "Degraded" {
result.ErrorCount++
}
}
result.ScanTime = time.Since(start).Round(time.Millisecond).String()
result.ScannedAt = time.Now()
return result
}
func (s *Scanner) enumDriversPnP() []Driver {
// Use PowerShell to enumerate PnP signed drivers via WMI
psScript := `
Get-CimInstance Win32_PnPSignedDriver | Where-Object { $_.DeviceName -ne $null } | Select-Object -First 100 DeviceName, DeviceClass, Manufacturer, DriverVersion, DriverDate, InfName, IsSigned, Status | ConvertTo-Json -Compress
`
out, err := exec.Command("powershell", "-NoProfile", "-Command", psScript).Output()
if err != nil {
return []Driver{}
}
var raw []struct {
DeviceName string `json:"DeviceName"`
DeviceClass string `json:"DeviceClass"`
Manufacturer string `json:"Manufacturer"`
DriverVersion string `json:"DriverVersion"`
DriverDate string `json:"DriverDate"`
InfName string `json:"InfName"`
IsSigned bool `json:"IsSigned"`
Status string `json:"Status"`
}
if err := json.Unmarshal(out, &raw); err != nil {
// Try as single object (when only 1 result)
var single struct {
DeviceName string `json:"DeviceName"`
DeviceClass string `json:"DeviceClass"`
Manufacturer string `json:"Manufacturer"`
DriverVersion string `json:"DriverVersion"`
DriverDate string `json:"DriverDate"`
InfName string `json:"InfName"`
IsSigned bool `json:"IsSigned"`
Status string `json:"Status"`
}
if err := json.Unmarshal(out, &single); err != nil {
return []Driver{}
}
raw = append(raw, single)
}
drivers := make([]Driver, 0, len(raw))
for _, r := range raw {
d := Driver{
DeviceName: r.DeviceName,
DeviceClass: normalizeClass(r.DeviceClass),
Manufacturer: r.Manufacturer,
DriverVersion: r.DriverVersion,
DriverDate: formatDriverDate(r.DriverDate),
InfName: r.InfName,
IsSigned: r.IsSigned,
Status: r.Status,
}
d.NeedsUpdate = checkIfOutdated(r.DriverDate)
drivers = append(drivers, d)
}
return drivers
}
func normalizeClass(class string) string {
if class == "" {
return "Other"
}
return class
}
func formatDriverDate(date string) string {
if date == "" {
return "Unknown"
}
// WMI returns dates like "20230115000000.000000-000" or "/Date(1234567890000)/"
if strings.Contains(date, "/Date(") {
return date // Will be parsed on frontend
}
if len(date) >= 8 {
return date[:4] + "-" + date[4:6] + "-" + date[6:8]
}
return date
}
func checkIfOutdated(dateStr string) bool {
if dateStr == "" {
return false
}
// Consider drivers older than 2 years as potentially outdated
if len(dateStr) >= 8 {
t, err := time.Parse("20060102", dateStr[:8])
if err != nil {
return false
}
return time.Since(t) > 2*365*24*time.Hour
}
return false
}

156
internal/sysinfo/sysinfo.go Normal file
View File

@@ -0,0 +1,156 @@
package sysinfo
import (
"fmt"
"os/exec"
"runtime"
"strings"
"syscall"
"unsafe"
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procGlobalMemoryEx = kernel32.NewProc("GlobalMemoryStatusEx")
procGetDiskFreeSpaceA = kernel32.NewProc("GetDiskFreeSpaceExA")
procGetComputerNameW = kernel32.NewProc("GetComputerNameW")
ntdll = syscall.NewLazyDLL("ntdll.dll")
procRtlGetVersion = ntdll.NewProc("RtlGetVersion")
)
type Info struct {
ComputerName string `json:"computerName"`
OSName string `json:"osName"`
OSVersion string `json:"osVersion"`
OSBuild string `json:"osBuild"`
Architecture string `json:"architecture"`
CPUName string `json:"cpuName"`
CPUCores int `json:"cpuCores"`
TotalRAM string `json:"totalRam"`
UsedRAM string `json:"usedRam"`
FreeRAM string `json:"freeRam"`
RAMPercent int `json:"ramPercent"`
DiskTotal string `json:"diskTotal"`
DiskFree string `json:"diskFree"`
DiskUsed string `json:"diskUsed"`
DiskPercent int `json:"diskPercent"`
}
type memoryStatusEx struct {
Length uint32
MemoryLoad uint32
TotalPhys uint64
AvailPhys uint64
TotalPageFile uint64
AvailPageFile uint64
TotalVirtual uint64
AvailVirtual uint64
AvailExtendedVirtual uint64
}
type osVersionInfoExW struct {
OSVersionInfoSize uint32
MajorVersion uint32
MinorVersion uint32
BuildNumber uint32
PlatformId uint32
CSDVersion [128]uint16
}
type Collector struct{}
func NewCollector() *Collector {
return &Collector{}
}
func (c *Collector) Collect() Info {
info := Info{
Architecture: runtime.GOARCH,
CPUCores: runtime.NumCPU(),
}
info.ComputerName = getComputerName()
info.CPUName = getCPUName()
// OS version via RtlGetVersion (accurate, not subject to compatibility shims)
var osvi osVersionInfoExW
osvi.OSVersionInfoSize = uint32(unsafe.Sizeof(osvi))
procRtlGetVersion.Call(uintptr(unsafe.Pointer(&osvi)))
info.OSVersion = fmt.Sprintf("%d.%d", osvi.MajorVersion, osvi.MinorVersion)
info.OSBuild = fmt.Sprintf("%d", osvi.BuildNumber)
info.OSName = getWindowsProductName()
// Memory
var mem memoryStatusEx
mem.Length = uint32(unsafe.Sizeof(mem))
procGlobalMemoryEx.Call(uintptr(unsafe.Pointer(&mem)))
info.TotalRAM = formatBytes(mem.TotalPhys)
info.FreeRAM = formatBytes(mem.AvailPhys)
info.UsedRAM = formatBytes(mem.TotalPhys - mem.AvailPhys)
info.RAMPercent = int(mem.MemoryLoad)
// Disk (C: drive)
var freeBytesAvailable, totalBytes, totalFreeBytes uint64
cDrive, _ := syscall.BytePtrFromString("C:\\")
procGetDiskFreeSpaceA.Call(
uintptr(unsafe.Pointer(cDrive)),
uintptr(unsafe.Pointer(&freeBytesAvailable)),
uintptr(unsafe.Pointer(&totalBytes)),
uintptr(unsafe.Pointer(&totalFreeBytes)),
)
info.DiskTotal = formatBytes(totalBytes)
info.DiskFree = formatBytes(totalFreeBytes)
info.DiskUsed = formatBytes(totalBytes - totalFreeBytes)
if totalBytes > 0 {
info.DiskPercent = int(((totalBytes - totalFreeBytes) * 100) / totalBytes)
}
return info
}
func getComputerName() string {
var size uint32 = 256
buf := make([]uint16, size)
procGetComputerNameW.Call(uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&size)))
return syscall.UTF16ToString(buf[:size])
}
func getCPUName() string {
out, err := exec.Command("powershell", "-NoProfile", "-Command",
"(Get-CimInstance Win32_Processor).Name").Output()
if err != nil {
return "Unknown CPU"
}
return strings.TrimSpace(string(out))
}
func getWindowsProductName() string {
out, err := exec.Command("powershell", "-NoProfile", "-Command",
"(Get-CimInstance Win32_OperatingSystem).Caption").Output()
if err != nil {
return "Windows"
}
return strings.TrimSpace(string(out))
}
func formatBytes(b uint64) string {
const (
KB = 1024
MB = KB * 1024
GB = MB * 1024
TB = GB * 1024
)
switch {
case b >= TB:
return fmt.Sprintf("%.1f TB", float64(b)/float64(TB))
case b >= GB:
return fmt.Sprintf("%.1f GB", float64(b)/float64(GB))
case b >= MB:
return fmt.Sprintf("%.1f MB", float64(b)/float64(MB))
case b >= KB:
return fmt.Sprintf("%.1f KB", float64(b)/float64(KB))
default:
return fmt.Sprintf("%d B", b)
}
}

View File

@@ -0,0 +1,208 @@
package winupdate
import (
"encoding/json"
"os/exec"
"time"
)
type Update struct {
Title string `json:"title"`
Description string `json:"description"`
KBArticle string `json:"kbArticle"`
Category string `json:"category"`
Size string `json:"size"`
IsInstalled bool `json:"isInstalled"`
IsDownloaded bool `json:"isDownloaded"`
IsMandatory bool `json:"isMandatory"`
Severity string `json:"severity"`
}
type CheckResult struct {
Updates []Update `json:"updates"`
PendingCount int `json:"pendingCount"`
InstalledCount int `json:"installedCount"`
CheckTime string `json:"checkTime"`
CheckedAt time.Time `json:"checkedAt"`
Error string `json:"error,omitempty"`
}
type InstallResult struct {
Success bool `json:"success"`
Message string `json:"message"`
}
type Checker struct{}
func NewChecker() *Checker {
return &Checker{}
}
func (c *Checker) Check() CheckResult {
start := time.Now()
result := CheckResult{
Updates: []Update{},
}
// Use PowerShell COM object for Windows Update Agent
psScript := `
$ErrorActionPreference = 'SilentlyContinue'
$Session = New-Object -ComObject Microsoft.Update.Session
$Searcher = $Session.CreateUpdateSearcher()
# Get installed update history (recent 50)
$HistoryCount = $Searcher.GetTotalHistoryCount()
$History = $Searcher.QueryHistory(0, [Math]::Min($HistoryCount, 50))
$installed = @()
foreach ($entry in $History) {
if ($entry.Title) {
$installed += @{
Title = $entry.Title
Description = $entry.Description
Date = $entry.Date.ToString("yyyy-MM-dd")
ResultCode = $entry.ResultCode
}
}
}
# Search for pending updates
try {
$SearchResult = $Searcher.Search("IsInstalled=0")
$pending = @()
foreach ($update in $SearchResult.Updates) {
$kb = ""
if ($update.KBArticleIDs.Count -gt 0) { $kb = "KB" + $update.KBArticleIDs.Item(0) }
$cat = ""
if ($update.Categories.Count -gt 0) { $cat = $update.Categories.Item(0).Name }
$sev = if ($update.MsrcSeverity) { $update.MsrcSeverity } else { "Unspecified" }
$sz = ""
if ($update.MaxDownloadSize -gt 0) {
$mb = [Math]::Round($update.MaxDownloadSize / 1MB, 1)
$sz = "$mb MB"
}
$pending += @{
Title = $update.Title
Description = $update.Description
KBArticle = $kb
Category = $cat
Size = $sz
IsDownloaded = $update.IsDownloaded
IsMandatory = $update.IsMandatory
Severity = $sev
}
}
} catch {
$pending = @()
}
@{
Installed = $installed
Pending = $pending
} | ConvertTo-Json -Depth 3 -Compress
`
out, err := exec.Command("powershell", "-NoProfile", "-Command", psScript).Output()
if err != nil {
result.Error = "Failed to check updates: " + err.Error()
result.CheckTime = time.Since(start).Round(time.Millisecond).String()
result.CheckedAt = time.Now()
return result
}
var raw struct {
Installed []struct {
Title string `json:"Title"`
Description string `json:"Description"`
Date string `json:"Date"`
ResultCode int `json:"ResultCode"`
} `json:"Installed"`
Pending []struct {
Title string `json:"Title"`
Description string `json:"Description"`
KBArticle string `json:"KBArticle"`
Category string `json:"Category"`
Size string `json:"Size"`
IsDownloaded bool `json:"IsDownloaded"`
IsMandatory bool `json:"IsMandatory"`
Severity string `json:"Severity"`
} `json:"Pending"`
}
if err := json.Unmarshal(out, &raw); err != nil {
result.Error = "Failed to parse update data"
result.CheckTime = time.Since(start).Round(time.Millisecond).String()
result.CheckedAt = time.Now()
return result
}
for _, p := range raw.Pending {
result.Updates = append(result.Updates, Update{
Title: p.Title,
Description: p.Description,
KBArticle: p.KBArticle,
Category: p.Category,
Size: p.Size,
IsInstalled: false,
IsDownloaded: p.IsDownloaded,
IsMandatory: p.IsMandatory,
Severity: p.Severity,
})
result.PendingCount++
}
for _, i := range raw.Installed {
result.Updates = append(result.Updates, Update{
Title: i.Title,
Description: i.Description,
IsInstalled: true,
Category: "Installed",
})
result.InstalledCount++
}
result.CheckTime = time.Since(start).Round(time.Millisecond).String()
result.CheckedAt = time.Now()
return result
}
func (c *Checker) Install(titles []string) InstallResult {
// Trigger Windows Update installation via PowerShell
psScript := `
$Session = New-Object -ComObject Microsoft.Update.Session
$Searcher = $Session.CreateUpdateSearcher()
$SearchResult = $Searcher.Search("IsInstalled=0")
$ToInstall = New-Object -ComObject Microsoft.Update.UpdateColl
foreach ($update in $SearchResult.Updates) {
if ($update.IsDownloaded -eq $false) {
$Downloader = $Session.CreateUpdateDownloader()
$dl = New-Object -ComObject Microsoft.Update.UpdateColl
$dl.Add($update) | Out-Null
$Downloader.Updates = $dl
$Downloader.Download() | Out-Null
}
$ToInstall.Add($update) | Out-Null
}
if ($ToInstall.Count -gt 0) {
$Installer = $Session.CreateUpdateInstaller()
$Installer.Updates = $ToInstall
$Result = $Installer.Install()
Write-Output "Installed $($ToInstall.Count) updates. Result: $($Result.ResultCode)"
} else {
Write-Output "No updates to install."
}
`
out, err := exec.Command("powershell", "-NoProfile", "-Command", psScript).Output()
if err != nil {
return InstallResult{
Success: false,
Message: "Installation failed: " + err.Error(),
}
}
return InstallResult{
Success: true,
Message: string(out),
}
}