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:
parent
0c45e77911
commit
c9b0ae5b89
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user