Add self-signed cert + catalog approach for driver installation

New flow: PowerShell New-SelfSignedCertificate → install to
TrustedPublisher/Root stores → create .cat via inf2cat/makecat/
New-FileCatalog → sign with signtool/Set-AuthenticodeSignature →
pnputil /install. Falls back to SetupAPI DIF_INSTALLDEVICE.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jim800121chen 2026-03-10 00:41:19 +08:00
parent c9b0ae5b89
commit 9ec1926133
2 changed files with 259 additions and 209 deletions

View File

@ -1,13 +1,15 @@
// Test tool for Windows USB driver installation.
// Build: GOOS=windows GOARCH=amd64 go build -o test_driver_install.exe .
// Run on Windows (as admin):
// .\test_driver_install.exe "C:\Users\User\AppData\Local\EdgeAIPlatform\drivers\kneron_winusb.inf"
// .\test_driver_install.exe "C:\Users\User\AppData\Local\EdgeAIPlatform\drivers"
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"unsafe"
@ -15,7 +17,6 @@ import (
var (
setupapi = syscall.MustLoadDLL("setupapi.dll")
newdev = syscall.MustLoadDLL("newdev.dll")
procSetupDiGetClassDevsW = setupapi.MustFindProc("SetupDiGetClassDevsW")
procSetupDiEnumDeviceInfo = setupapi.MustFindProc("SetupDiEnumDeviceInfo")
@ -25,18 +26,14 @@ var (
procSetupDiCallClassInstaller = setupapi.MustFindProc("SetupDiCallClassInstaller")
procSetupDiDestroyDevInfoList = setupapi.MustFindProc("SetupDiDestroyDeviceInfoList")
procSetupDiBuildDriverInfoList = setupapi.MustFindProc("SetupDiBuildDriverInfoList")
procUpdateDriverForPlugAndPlayDevicesW = newdev.MustFindProc("UpdateDriverForPlugAndPlayDevicesW")
procDiInstallDriverW = newdev.MustFindProc("DiInstallDriverW")
)
const (
DIGCF_PRESENT = 0x00000002
DIGCF_ALLCLASSES = 0x00000004
SPDRP_HARDWAREID = 0x00000001
SPDRP_COMPATIBLEIDS = 0x00000002
SPDRP_DEVICEDESC = 0x00000000
SPDRP_HARDWAREID = 0x00000001
SPDRP_DEVICEDESC = 0x00000000
DIF_SELECTBESTCOMPATDRV = 0x00000017
DIF_INSTALLDEVICE = 0x00000002
@ -44,8 +41,6 @@ const (
DI_FLAGSEX_ALLOWEXCLUDEDDRVS = 0x00000800
DI_ENUMSINGLEINF = 0x00010000
INSTALLFLAG_FORCE = 0x00000001
DIIRFLAG_FORCE_INF = 0x00000002
SPDIT_COMPATDRIVER = 0x00000002
INVALID_HANDLE_VALUE = ^uintptr(0)
@ -71,10 +66,9 @@ type SP_DEVINSTALL_PARAMS_W struct {
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
count := size / 2
if count == 0 {
return result
}
@ -92,50 +86,237 @@ func multiSzToStrings(buf []uint16, size uint32) []string {
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: test_driver_install.exe <path-to-inf>")
fmt.Println(" test_driver_install.exe --list")
fmt.Println("Usage: test_driver_install.exe <driver-directory>")
fmt.Println(" The directory should contain kneron_winusb.inf and amd64\\ co-installers")
os.Exit(1)
}
driverDir := os.Args[1]
infPath := filepath.Join(driverDir, "kneron_winusb.inf")
fmt.Printf("Driver dir: %s\n", driverDir)
fmt.Printf("INF path: %s\n", infPath)
if _, err := os.Stat(infPath); err != nil {
fmt.Printf("ERROR: %s not found: %v\n", infPath, err)
os.Exit(1)
}
targetHWID := "USB\\VID_3231&PID_0100"
if os.Args[1] == "--list" {
listAllDevices(targetHWID)
return
}
// Step 1: List devices
fmt.Println("\n=== Step 1: List Kneron USB devices ===")
listKneronDevices(targetHWID)
infPath := os.Args[1]
fmt.Printf("INF path: %s\n", infPath)
// Step 2: Self-sign approach — create cert, create cat, sign, install
fmt.Println("\n=== Step 2: Create self-signed cert + catalog, then install ===")
certPath := filepath.Join(driverDir, "kneron_selfsign.cer")
if _, err := os.Stat(infPath); err != nil {
fmt.Printf("ERROR: file not found: %v\n", err)
// 2a: Create self-signed code signing cert via PowerShell
fmt.Println("[2a] Creating self-signed code signing certificate...")
thumbprint, err := createSelfSignedCert(certPath)
if err != nil {
fmt.Printf(" FAILED: %v\n", err)
os.Exit(1)
}
fmt.Printf(" Thumbprint: %s\n", thumbprint)
fmt.Println("")
fmt.Println("=== Step 1: List USB devices matching VID_3231 ===")
listAllDevices(targetHWID)
// 2b: Install cert to TrustedPublisher store
fmt.Println("[2b] Installing cert to TrustedPublisher + Root stores...")
installCertToStore(certPath, "TrustedPublisher")
installCertToStore(certPath, "Root")
fmt.Println("")
fmt.Println("=== Step 2: UpdateDriverForPlugAndPlayDevicesW ===")
tryUpdateDriver(infPath, targetHWID)
// 2c: Create .cat file using makecat.exe (from system) or manual approach
catPath := filepath.Join(driverDir, "kneron_winusb.cat")
fmt.Println("[2c] Creating catalog file...")
err = createCatalog(driverDir, infPath, catPath)
if err != nil {
fmt.Printf(" Catalog creation failed: %v\n", err)
fmt.Println(" Trying alternative approach without catalog...")
}
fmt.Println("")
fmt.Println("=== Step 3: DiInstallDriverW ===")
tryDiInstallDriver(infPath)
// 2d: Sign the catalog
if _, err := os.Stat(catPath); err == nil {
fmt.Println("[2d] Signing catalog file...")
signCatalog(catPath, thumbprint)
}
fmt.Println("")
fmt.Println("=== Step 4: SetupAPI direct device node install ===")
trySetupAPIInstall(infPath, targetHWID)
// 2e: Try pnputil with signed catalog
fmt.Println("[2e] Installing driver with pnputil...")
cmd := exec.Command("pnputil", "/add-driver", infPath, "/install")
out, err := cmd.CombinedOutput()
fmt.Printf(" pnputil: %s\n", strings.TrimSpace(string(out)))
if err != nil {
fmt.Printf(" pnputil error: %v\n", err)
// Fallback: try SetupAPI
fmt.Println("\n[2f] Fallback: SetupAPI DIF_INSTALLDEVICE...")
trySetupAPIInstall(infPath, targetHWID)
} else {
fmt.Println(" SUCCESS via pnputil!")
}
fmt.Println("\n=== Done ===")
}
func listAllDevices(targetHWID string) {
hDevInfo, _, err := procSetupDiGetClassDevsW.Call(
0, 0, 0,
DIGCF_PRESENT|DIGCF_ALLCLASSES,
)
func createSelfSignedCert(certPath string) (string, error) {
psScript := fmt.Sprintf(`
$ErrorActionPreference = 'Stop'
# Remove old cert if exists
Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Subject -eq 'CN=Kneron Edge AI Platform' } | Remove-Item -Force -ErrorAction SilentlyContinue
# Create new self-signed code signing cert
$cert = New-SelfSignedCertificate -Type CodeSigningCert -Subject "CN=Kneron Edge AI Platform" -CertStoreLocation Cert:\LocalMachine\My -NotAfter (Get-Date).AddYears(10) -FriendlyName "Kneron Edge AI Platform Driver Signing"
# Export DER cert
Export-Certificate -Cert $cert -FilePath '%s' -Force | Out-Null
Write-Output $cert.Thumbprint
`, certPath)
cmd := exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", psScript)
out, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("powershell: %v\nOutput: %s", err, string(out))
}
thumbprint := strings.TrimSpace(string(out))
// Extract just the thumbprint (last line)
lines := strings.Split(thumbprint, "\n")
for i := len(lines) - 1; i >= 0; i-- {
line := strings.TrimSpace(lines[i])
if len(line) == 40 { // SHA1 thumbprint length
return line, nil
}
}
return thumbprint, nil
}
func installCertToStore(certPath, storeName string) {
cmd := exec.Command("certutil", "-addstore", storeName, certPath)
out, err := cmd.CombinedOutput()
outStr := strings.TrimSpace(string(out))
if err != nil {
fmt.Printf(" certutil %s: FAILED - %v\n %s\n", storeName, err, outStr)
} else {
fmt.Printf(" certutil %s: OK\n", storeName)
}
}
func createCatalog(driverDir, infPath, catPath string) error {
// Method 1: Try inf2cat from Windows SDK
sdkBases := []string{
`C:\Program Files (x86)\Windows Kits\10\bin`,
}
sdkVersions := []string{
"10.0.26100.0", "10.0.22621.0", "10.0.22000.0", "10.0.19041.0", "10.0.18362.0",
}
for _, base := range sdkBases {
for _, ver := range sdkVersions {
inf2cat := filepath.Join(base, ver, "x86", "inf2cat.exe")
if _, err := os.Stat(inf2cat); err == nil {
fmt.Printf(" Found inf2cat: %s\n", inf2cat)
cmd := exec.Command(inf2cat, "/driver:"+driverDir, "/os:10_X64", "/verbose")
out, err := cmd.CombinedOutput()
outStr := strings.TrimSpace(string(out))
fmt.Printf(" inf2cat output: %s\n", outStr)
if err == nil {
if _, err := os.Stat(catPath); err == nil {
return nil
}
}
fmt.Printf(" inf2cat error: %v\n", err)
}
}
}
// Method 2: Create a minimal catalog using MakeCat.exe definition file
fmt.Println(" inf2cat not found, trying MakeCat approach...")
cdfPath := filepath.Join(driverDir, "kneron_winusb.cdf")
cdfContent := fmt.Sprintf(`[CatalogHeader]
Name=%s
PublicVersion=0x0000001
EncodingType=0x00010001
CATATTR1=0x10010001:OSAttr:2:10.0
[CatalogFiles]
kneron_winusb.inf=%s
`, catPath, infPath)
if err := os.WriteFile(cdfPath, []byte(cdfContent), 0644); err != nil {
return fmt.Errorf("write CDF: %w", err)
}
defer os.Remove(cdfPath)
cmd := exec.Command("makecat", "-v", cdfPath)
out, err := cmd.CombinedOutput()
outStr := strings.TrimSpace(string(out))
fmt.Printf(" makecat output: %s\n", outStr)
if err != nil {
fmt.Printf(" makecat error: %v\n", err)
// Method 3: Use PowerShell to create catalog via .NET
fmt.Println(" Trying PowerShell New-FileCatalog...")
psCmd := fmt.Sprintf(`New-FileCatalog -Path '%s' -CatalogFilePath '%s' -CatalogVersion 2.0`, driverDir, catPath)
cmd = exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", psCmd)
out, err = cmd.CombinedOutput()
outStr = strings.TrimSpace(string(out))
fmt.Printf(" New-FileCatalog: %s\n", outStr)
if err != nil {
return fmt.Errorf("all catalog creation methods failed")
}
}
if _, err := os.Stat(catPath); err == nil {
return nil
}
return fmt.Errorf("catalog file not created")
}
func signCatalog(catPath, thumbprint string) {
// Try signtool from Windows SDK
sdkBases := []string{`C:\Program Files (x86)\Windows Kits\10\bin`}
sdkVersions := []string{
"10.0.26100.0", "10.0.22621.0", "10.0.22000.0", "10.0.19041.0", "10.0.18362.0",
}
for _, base := range sdkBases {
for _, ver := range sdkVersions {
signtool := filepath.Join(base, ver, "x64", "signtool.exe")
if _, err := os.Stat(signtool); err == nil {
fmt.Printf(" Found signtool: %s\n", signtool)
cmd := exec.Command(signtool, "sign", "/sha1", thumbprint, "/fd", "SHA256",
"/s", "My", "/sm", "/v", catPath)
out, err := cmd.CombinedOutput()
outStr := strings.TrimSpace(string(out))
fmt.Printf(" signtool: %s\n", outStr)
if err != nil {
fmt.Printf(" signtool error: %v\n", err)
} else {
return
}
}
}
}
// Fallback: PowerShell Set-AuthenticodeSignature
fmt.Println(" signtool not found, trying Set-AuthenticodeSignature...")
psCmd := fmt.Sprintf(`
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Thumbprint -eq '%s' }
if ($cert) {
Set-AuthenticodeSignature -FilePath '%s' -Certificate $cert -HashAlgorithm SHA256
} else {
Write-Error "Certificate not found"
}
`, thumbprint, catPath)
cmd := exec.Command("powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", psCmd)
out, err := cmd.CombinedOutput()
fmt.Printf(" PowerShell sign: %s\n", strings.TrimSpace(string(out)))
if err != nil {
fmt.Printf(" PowerShell sign error: %v\n", err)
}
}
func listKneronDevices(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)
fmt.Printf("ERROR: %v\n", err)
return
}
defer procSetupDiDestroyDevInfoList.Call(hDevInfo)
@ -143,41 +324,28 @@ func listAllDevices(targetHWID string) {
var devInfoData SP_DEVINFO_DATA
devInfoData.cbSize = uint32(unsafe.Sizeof(devInfoData))
total := 0
matched := 0
total, matched := 0, 0
for i := uint32(0); ; i++ {
ret, _, _ := procSetupDiEnumDeviceInfo.Call(
hDevInfo, uintptr(i),
uintptr(unsafe.Pointer(&devInfoData)),
)
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)),
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") {
if strings.Contains(strings.ToUpper(id), "VID_3231") {
isKneron = true
break
}
@ -185,95 +353,34 @@ func listAllDevices(targetHWID string) {
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)),
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)"
desc := "(unknown)"
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")
fmt.Printf(" #%d: %s (DevInst: %d)\n", matched, desc, devInfoData.DevInst)
for _, id := range hwids {
match := ""
tag := ""
if strings.EqualFold(id, targetHWID) {
match = " <== MATCH"
tag = " <== MATCH"
}
fmt.Printf(" - %s%s\n", id, match)
fmt.Printf(" %s%s\n", id, tag)
}
}
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)
fmt.Println("Calling UpdateDriverForPlugAndPlayDevicesW...")
ret, _, lastErr := procUpdateDriverForPlugAndPlayDevicesW.Call(
0,
uintptr(unsafe.Pointer(hwIDUTF16)),
uintptr(unsafe.Pointer(infUTF16)),
INSTALLFLAG_FORCE,
uintptr(unsafe.Pointer(&needReboot)),
)
fmt.Printf("Return value: %d\n", ret)
if ret == 0 {
errno := lastErr.(syscall.Errno)
fmt.Printf("FAILED - error: %v (code: %d / 0x%08X)\n", lastErr, uint32(errno), uint32(errno))
} else {
fmt.Println("SUCCESS!")
}
}
func tryDiInstallDriver(infPath string) {
infUTF16, _ := syscall.UTF16PtrFromString(infPath)
var needReboot int32
fmt.Println("Calling DiInstallDriverW...")
ret, _, lastErr := procDiInstallDriverW.Call(
0,
uintptr(unsafe.Pointer(infUTF16)),
DIIRFLAG_FORCE_INF,
uintptr(unsafe.Pointer(&needReboot)),
)
fmt.Printf("Return value: %d\n", ret)
if ret == 0 {
errno := lastErr.(syscall.Errno)
fmt.Printf("FAILED - error: %v (code: %d / 0x%08X)\n", lastErr, uint32(errno), uint32(errno))
} else {
fmt.Println("SUCCESS!")
}
fmt.Printf(" Total: %d, Kneron: %d\n", total, matched)
}
func trySetupAPIInstall(infPath, targetHWID string) {
fmt.Println("Enumerating present USB devices...")
hDevInfo, _, err := procSetupDiGetClassDevsW.Call(
0, 0, 0,
DIGCF_PRESENT|DIGCF_ALLCLASSES,
)
hDevInfo, _, err := procSetupDiGetClassDevsW.Call(0, 0, 0, DIGCF_PRESENT|DIGCF_ALLCLASSES)
if hDevInfo == INVALID_HANDLE_VALUE {
fmt.Printf("ERROR: SetupDiGetClassDevsW: %v\n", err)
fmt.Printf(" ERROR: %v\n", err)
return
}
defer procSetupDiDestroyDevInfoList.Call(hDevInfo)
@ -281,124 +388,67 @@ func trySetupAPIInstall(infPath, targetHWID string) {
var devInfoData SP_DEVINFO_DATA
devInfoData.cbSize = uint32(unsafe.Sizeof(devInfoData))
found := false
for i := uint32(0); ; i++ {
ret, _, _ := procSetupDiEnumDeviceInfo.Call(
hDevInfo, uintptr(i),
uintptr(unsafe.Pointer(&devInfoData)),
)
ret, _, _ := procSetupDiEnumDeviceInfo.Call(hDevInfo, uintptr(i), uintptr(unsafe.Pointer(&devInfoData)))
if ret == 0 {
break
fmt.Println(" Target device not found")
return
}
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)),
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)
matchFound := false
match := false
for _, id := range hwids {
if strings.EqualFold(id, targetHWID) {
matchFound = true
match = true
break
}
}
if !matchFound {
if !match {
continue
}
fmt.Printf("FOUND target device (DevInst: %d)\n", devInfoData.DevInst)
found = true
fmt.Printf(" Found device (DevInst: %d)\n", devInfoData.DevInst)
// 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)
procSetupDiGetDevInstallParamsW.Call(hDevInfo, uintptr(unsafe.Pointer(&devInfoData)), uintptr(unsafe.Pointer(&installParams)))
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)),
)
procSetupDiSetDevInstallParamsW.Call(hDevInfo, uintptr(unsafe.Pointer(&devInfoData)), uintptr(unsafe.Pointer(&installParams)))
ret, _, _ = procSetupDiBuildDriverInfoList.Call(hDevInfo, uintptr(unsafe.Pointer(&devInfoData)), SPDIT_COMPATDRIVER)
if ret == 0 {
fmt.Printf("ERROR: SetDeviceInstallParams: %v\n", err)
fmt.Println(" BuildDriverInfoList failed")
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,
)
ret, _, _ = procSetupDiCallClassInstaller.Call(DIF_SELECTBESTCOMPATDRV, hDevInfo, uintptr(unsafe.Pointer(&devInfoData)))
if ret == 0 {
errno := err.(syscall.Errno)
fmt.Printf("ERROR: BuildDriverInfoList: %v (0x%08X)\n", err, uint32(errno))
fmt.Println(" DIF_SELECTBESTCOMPATDRV failed")
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)),
)
ret, _, err = procSetupDiCallClassInstaller.Call(DIF_INSTALLDEVICE, 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))
fmt.Printf(" DIF_INSTALLDEVICE FAILED: %v (0x%08X)\n", err, uint32(errno))
} else {
fmt.Println("SUCCESS - driver installed!")
fmt.Println(" SUCCESS - driver installed!")
}
break
}
if !found {
fmt.Println("Target device NOT found among present devices")
return
}
}