Improve test tool: proper REG_MULTI_SZ parsing and device enumeration debug

- Parse MULTI_SZ hardware IDs correctly (was only reading first string)
- Add device listing step to show all Kneron devices with Hardware IDs
- Case-insensitive HWID matching
- Better debug output at each SetupAPI step

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jim800121chen 2026-03-10 00:33:55 +08:00
parent 0c45e77911
commit c9b0ae5b89
2 changed files with 238 additions and 128 deletions

View File

@ -8,6 +8,7 @@ package main
import (
"fmt"
"os"
"strings"
"syscall"
"unsafe"
)
@ -19,7 +20,6 @@ var (
procSetupDiGetClassDevsW = setupapi.MustFindProc("SetupDiGetClassDevsW")
procSetupDiEnumDeviceInfo = setupapi.MustFindProc("SetupDiEnumDeviceInfo")
procSetupDiGetDeviceRegistryPropW = setupapi.MustFindProc("SetupDiGetDeviceRegistryPropertyW")
procSetupDiSetDeviceRegistryPropW = setupapi.MustFindProc("SetupDiSetDeviceRegistryPropertyW")
procSetupDiSetDevInstallParamsW = setupapi.MustFindProc("SetupDiSetDeviceInstallParamsW")
procSetupDiGetDevInstallParamsW = setupapi.MustFindProc("SetupDiGetDeviceInstallParamsW")
procSetupDiCallClassInstaller = setupapi.MustFindProc("SetupDiCallClassInstaller")
@ -34,23 +34,23 @@ const (
DIGCF_PRESENT = 0x00000002
DIGCF_ALLCLASSES = 0x00000004
SPDRP_HARDWAREID = 0x00000001
SPDRP_HARDWAREID = 0x00000001
SPDRP_COMPATIBLEIDS = 0x00000002
SPDRP_DEVICEDESC = 0x00000000
DIF_SELECTBESTCOMPATDRV = 0x00000017
DIF_INSTALLDEVICE = 0x00000002
DIF_INSTALLDEVICE = 0x00000002
DI_FLAGSEX_ALLOWEXCLUDEDDRVS = 0x00000800
DI_ENUMSINGLEINF = 0x00010000
DI_DONOTCALLCONFIGMG = 0x00000002
INSTALLFLAG_FORCE = 0x00000001
DIIRFLAG_FORCE_INF = 0x00000002
SPDIT_COMPATDRIVER = 0x00000002
INSTALLFLAG_FORCE = 0x00000001
DIIRFLAG_FORCE_INF = 0x00000002
SPDIT_COMPATDRIVER = 0x00000002
INVALID_HANDLE_VALUE = ^uintptr(0)
)
// SP_DEVINFO_DATA
type SP_DEVINFO_DATA struct {
cbSize uint32
ClassGuid [16]byte
@ -58,26 +58,52 @@ type SP_DEVINFO_DATA struct {
Reserved uintptr
}
// SP_DEVINSTALL_PARAMS_W (size depends on arch: 584 bytes on amd64)
type SP_DEVINSTALL_PARAMS_W struct {
cbSize uint32
Flags uint32
FlagsEx uint32
hwndParent uintptr
InstallMsgHandler uintptr
InstallMsgHandlerContext uintptr
FileQueue uintptr
ClassInstallReserved uintptr
Reserved uint32
DriverPath [260]uint16
cbSize uint32
Flags uint32
FlagsEx uint32
hwndParent uintptr
InstallMsgHandler uintptr
InstallMsgHandlerContext uintptr
FileQueue uintptr
ClassInstallReserved uintptr
Reserved uint32
DriverPath [260]uint16
}
// multiSzToStrings parses a REG_MULTI_SZ buffer into individual strings
func multiSzToStrings(buf []uint16, size uint32) []string {
var result []string
count := size / 2 // byte count to uint16 count
if count == 0 {
return result
}
start := 0
for i := 0; i < int(count); i++ {
if buf[i] == 0 {
if i > start {
result = append(result, syscall.UTF16ToString(buf[start:i]))
}
start = i + 1
}
}
return result
}
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: test_driver_install.exe <path-to-inf>")
fmt.Println(" test_driver_install.exe --list")
os.Exit(1)
}
targetHWID := "USB\\VID_3231&PID_0100"
if os.Args[1] == "--list" {
listAllDevices(targetHWID)
return
}
infPath := os.Args[1]
fmt.Printf("INF path: %s\n", infPath)
@ -86,25 +112,116 @@ func main() {
os.Exit(1)
}
hardwareID := "USB\\VID_3231&PID_0100"
fmt.Println("")
fmt.Println("=== Step 1: List USB devices matching VID_3231 ===")
listAllDevices(targetHWID)
fmt.Println("")
fmt.Println("=== Method 1: UpdateDriverForPlugAndPlayDevicesW ===")
tryUpdateDriver(infPath, hardwareID)
fmt.Println("=== Step 2: UpdateDriverForPlugAndPlayDevicesW ===")
tryUpdateDriver(infPath, targetHWID)
fmt.Println("")
fmt.Println("=== Method 2: DiInstallDriverW ===")
fmt.Println("=== Step 3: DiInstallDriverW ===")
tryDiInstallDriver(infPath)
fmt.Println("")
fmt.Println("=== Method 3: SetupAPI direct device node install ===")
trySetupAPIInstall(infPath, hardwareID)
fmt.Println("=== Step 4: SetupAPI direct device node install ===")
trySetupAPIInstall(infPath, targetHWID)
}
func listAllDevices(targetHWID string) {
hDevInfo, _, err := procSetupDiGetClassDevsW.Call(
0, 0, 0,
DIGCF_PRESENT|DIGCF_ALLCLASSES,
)
if hDevInfo == INVALID_HANDLE_VALUE {
fmt.Printf("ERROR: SetupDiGetClassDevsW: %v\n", err)
return
}
defer procSetupDiDestroyDevInfoList.Call(hDevInfo)
var devInfoData SP_DEVINFO_DATA
devInfoData.cbSize = uint32(unsafe.Sizeof(devInfoData))
total := 0
matched := 0
for i := uint32(0); ; i++ {
ret, _, _ := procSetupDiEnumDeviceInfo.Call(
hDevInfo, uintptr(i),
uintptr(unsafe.Pointer(&devInfoData)),
)
if ret == 0 {
break
}
total++
// Get hardware IDs (REG_MULTI_SZ)
var buf [2048]uint16
var dataType, reqSize uint32
ret, _, _ = procSetupDiGetDeviceRegistryPropW.Call(
hDevInfo,
uintptr(unsafe.Pointer(&devInfoData)),
SPDRP_HARDWAREID,
uintptr(unsafe.Pointer(&dataType)),
uintptr(unsafe.Pointer(&buf[0])),
uintptr(len(buf)*2),
uintptr(unsafe.Pointer(&reqSize)),
)
if ret == 0 {
continue
}
hwids := multiSzToStrings(buf[:], reqSize)
// Check if any HWID contains "VID_3231" or matches target
isKneron := false
for _, id := range hwids {
upper := strings.ToUpper(id)
if strings.Contains(upper, "VID_3231") {
isKneron = true
break
}
}
if !isKneron {
continue
}
matched++
// Get device description
var descBuf [512]uint16
ret, _, _ = procSetupDiGetDeviceRegistryPropW.Call(
hDevInfo,
uintptr(unsafe.Pointer(&devInfoData)),
SPDRP_DEVICEDESC,
uintptr(unsafe.Pointer(&dataType)),
uintptr(unsafe.Pointer(&descBuf[0])),
uintptr(len(descBuf)*2),
uintptr(unsafe.Pointer(&reqSize)),
)
desc := "(no description)"
if ret != 0 {
desc = syscall.UTF16ToString(descBuf[:])
}
fmt.Printf("\n Device #%d (DevInst: %d)\n", matched, devInfoData.DevInst)
fmt.Printf(" Description: %s\n", desc)
fmt.Printf(" Hardware IDs:\n")
for _, id := range hwids {
match := ""
if strings.EqualFold(id, targetHWID) {
match = " <== MATCH"
}
fmt.Printf(" - %s%s\n", id, match)
}
}
fmt.Printf("\nTotal devices enumerated: %d, Kneron devices found: %d\n", total, matched)
}
func tryUpdateDriver(infPath, hardwareID string) {
infUTF16, _ := syscall.UTF16PtrFromString(infPath)
hwIDUTF16, _ := syscall.UTF16PtrFromString(hardwareID)
var needReboot int32
fmt.Printf("Hardware ID: %s\n", hardwareID)
@ -132,7 +249,6 @@ func tryDiInstallDriver(infPath string) {
var needReboot int32
fmt.Println("Calling DiInstallDriverW...")
ret, _, lastErr := procDiInstallDriverW.Call(
0,
uintptr(unsafe.Pointer(infUTF16)),
@ -150,17 +266,14 @@ func tryDiInstallDriver(infPath string) {
}
func trySetupAPIInstall(infPath, targetHWID string) {
fmt.Println("Enumerating all present USB devices...")
fmt.Println("Enumerating present USB devices...")
// Get all present devices
hDevInfo, _, err := procSetupDiGetClassDevsW.Call(
0, // NULL = all classes
0, // no enumerator
0, // no hwnd
0, 0, 0,
DIGCF_PRESENT|DIGCF_ALLCLASSES,
)
if hDevInfo == INVALID_HANDLE_VALUE {
fmt.Printf("ERROR: SetupDiGetClassDevsW failed: %v\n", err)
fmt.Printf("ERROR: SetupDiGetClassDevsW: %v\n", err)
return
}
defer procSetupDiDestroyDevInfoList.Call(hDevInfo)
@ -171,16 +284,14 @@ func trySetupAPIInstall(infPath, targetHWID string) {
found := false
for i := uint32(0); ; i++ {
ret, _, _ := procSetupDiEnumDeviceInfo.Call(
hDevInfo,
uintptr(i),
hDevInfo, uintptr(i),
uintptr(unsafe.Pointer(&devInfoData)),
)
if ret == 0 {
break
}
// Get hardware ID
var buf [1024]uint16
var buf [2048]uint16
var dataType, reqSize uint32
ret, _, _ = procSetupDiGetDeviceRegistryPropW.Call(
hDevInfo,
@ -195,100 +306,99 @@ func trySetupAPIInstall(infPath, targetHWID string) {
continue
}
// MULTI_SZ: check each string
hwid := syscall.UTF16ToString(buf[:])
if hwid == targetHWID {
fmt.Printf("FOUND device: %s (DevInst: %d)\n", hwid, devInfoData.DevInst)
found = true
// Now try to install driver on this specific device
installOnDevice(hDevInfo, &devInfoData, infPath)
break
hwids := multiSzToStrings(buf[:], reqSize)
matchFound := false
for _, id := range hwids {
if strings.EqualFold(id, targetHWID) {
matchFound = true
break
}
}
if !matchFound {
continue
}
fmt.Printf("FOUND target device (DevInst: %d)\n", devInfoData.DevInst)
found = true
// Set install params: use our specific INF
var installParams SP_DEVINSTALL_PARAMS_W
installParams.cbSize = uint32(unsafe.Sizeof(installParams))
ret, _, err = procSetupDiGetDevInstallParamsW.Call(
hDevInfo,
uintptr(unsafe.Pointer(&devInfoData)),
uintptr(unsafe.Pointer(&installParams)),
)
if ret == 0 {
fmt.Printf("ERROR: GetDeviceInstallParams: %v\n", err)
return
}
fmt.Printf("Current Flags: 0x%08X, FlagsEx: 0x%08X\n", installParams.Flags, installParams.FlagsEx)
infUTF16, _ := syscall.UTF16FromString(infPath)
copy(installParams.DriverPath[:], infUTF16)
installParams.Flags |= DI_ENUMSINGLEINF
installParams.FlagsEx |= DI_FLAGSEX_ALLOWEXCLUDEDDRVS
ret, _, err = procSetupDiSetDevInstallParamsW.Call(
hDevInfo,
uintptr(unsafe.Pointer(&devInfoData)),
uintptr(unsafe.Pointer(&installParams)),
)
if ret == 0 {
fmt.Printf("ERROR: SetDeviceInstallParams: %v\n", err)
return
}
fmt.Println("Install params set OK")
// Build compatible driver list
fmt.Println("Building driver info list...")
ret, _, err = procSetupDiBuildDriverInfoList.Call(
hDevInfo,
uintptr(unsafe.Pointer(&devInfoData)),
SPDIT_COMPATDRIVER,
)
if ret == 0 {
errno := err.(syscall.Errno)
fmt.Printf("ERROR: BuildDriverInfoList: %v (0x%08X)\n", err, uint32(errno))
return
}
fmt.Println("Driver info list built OK")
// Select best compatible driver
fmt.Println("Selecting best compatible driver...")
ret, _, err = procSetupDiCallClassInstaller.Call(
DIF_SELECTBESTCOMPATDRV,
hDevInfo,
uintptr(unsafe.Pointer(&devInfoData)),
)
if ret == 0 {
errno := err.(syscall.Errno)
fmt.Printf("ERROR: DIF_SELECTBESTCOMPATDRV: %v (0x%08X)\n", err, uint32(errno))
return
}
fmt.Println("Best driver selected OK")
// Install device
fmt.Println("Installing driver on device...")
fmt.Println("(Windows Security dialog may appear)")
ret, _, err = procSetupDiCallClassInstaller.Call(
DIF_INSTALLDEVICE,
hDevInfo,
uintptr(unsafe.Pointer(&devInfoData)),
)
if ret == 0 {
errno := err.(syscall.Errno)
fmt.Printf("ERROR: DIF_INSTALLDEVICE: %v (0x%08X)\n", err, uint32(errno))
} else {
fmt.Println("SUCCESS - driver installed!")
}
break
}
if !found {
fmt.Printf("Device with Hardware ID '%s' not found among present devices.\n", targetHWID)
}
}
func installOnDevice(hDevInfo uintptr, devInfoData *SP_DEVINFO_DATA, infPath string) {
fmt.Println("Setting up driver install params...")
// Get current install params
var installParams SP_DEVINSTALL_PARAMS_W
installParams.cbSize = uint32(unsafe.Sizeof(installParams))
ret, _, err := procSetupDiGetDevInstallParamsW.Call(
hDevInfo,
uintptr(unsafe.Pointer(devInfoData)),
uintptr(unsafe.Pointer(&installParams)),
)
if ret == 0 {
fmt.Printf("ERROR: GetDeviceInstallParams failed: %v\n", err)
return
}
fmt.Printf("Current Flags: 0x%08X, FlagsEx: 0x%08X\n", installParams.Flags, installParams.FlagsEx)
// Set DriverPath to our INF and enable DI_ENUMSINGLEINF
infUTF16, _ := syscall.UTF16FromString(infPath)
copy(installParams.DriverPath[:], infUTF16)
installParams.Flags |= DI_ENUMSINGLEINF
installParams.FlagsEx |= DI_FLAGSEX_ALLOWEXCLUDEDDRVS
ret, _, err = procSetupDiSetDevInstallParamsW.Call(
hDevInfo,
uintptr(unsafe.Pointer(devInfoData)),
uintptr(unsafe.Pointer(&installParams)),
)
if ret == 0 {
fmt.Printf("ERROR: SetDeviceInstallParams failed: %v\n", err)
return
}
fmt.Println("Install params set (DI_ENUMSINGLEINF, DriverPath set)")
// Build driver info list from our specific INF
fmt.Println("Building driver info list from INF...")
ret, _, err = procSetupDiBuildDriverInfoList.Call(
hDevInfo,
uintptr(unsafe.Pointer(devInfoData)),
SPDIT_COMPATDRIVER,
)
if ret == 0 {
errno := err.(syscall.Errno)
fmt.Printf("ERROR: BuildDriverInfoList failed: %v (code: 0x%08X)\n", err, uint32(errno))
return
}
fmt.Println("Driver info list built.")
// Select best compatible driver
fmt.Println("Selecting best compatible driver...")
ret, _, err = procSetupDiCallClassInstaller.Call(
DIF_SELECTBESTCOMPATDRV,
hDevInfo,
uintptr(unsafe.Pointer(devInfoData)),
)
if ret == 0 {
errno := err.(syscall.Errno)
fmt.Printf("ERROR: DIF_SELECTBESTCOMPATDRV failed: %v (code: 0x%08X)\n", err, uint32(errno))
fmt.Println(" (This might mean the INF is not compatible or catalog issue)")
return
}
fmt.Println("Best compatible driver selected.")
// Install the device
fmt.Println("Installing device with selected driver...")
fmt.Println("(If a Windows Security dialog appears, click 'Install this driver software anyway')")
ret, _, err = procSetupDiCallClassInstaller.Call(
DIF_INSTALLDEVICE,
hDevInfo,
uintptr(unsafe.Pointer(devInfoData)),
)
if ret == 0 {
errno := err.(syscall.Errno)
fmt.Printf("ERROR: DIF_INSTALLDEVICE failed: %v (code: 0x%08X)\n", err, uint32(errno))
} else {
fmt.Println("SUCCESS - driver installed on device!")
fmt.Println("Target device NOT found among present devices")
}
}