From 3e8f2e3fa58b5825cfb3a2dbcf4591a882ecf92b Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Tue, 5 Feb 2019 16:29:17 +0100 Subject: [PATCH] setupapi: Add support for driver info lists Signed-off-by: Simon Rozman --- setupapi/setupapi_windows.go | 95 +++++++++++++++++++ setupapi/setupapi_windows_test.go | 63 +++++++++++++ setupapi/types_windows.go | 149 ++++++++++++++++++++++++++++++ setupapi/zsetupapi_windows.go | 91 ++++++++++++++++++ 4 files changed, 398 insertions(+) diff --git a/setupapi/setupapi_windows.go b/setupapi/setupapi_windows.go index 750a81b..84d324d 100644 --- a/setupapi/setupapi_windows.go +++ b/setupapi/setupapi_windows.go @@ -100,6 +100,101 @@ func (DeviceInfoSet DevInfo) Close() error { return SetupDiDestroyDeviceInfoList(DeviceInfoSet) } +//sys SetupDiBuildDriverInfoList(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, DriverType SPDIT) (err error) = setupapi.SetupDiBuildDriverInfoList + +// 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. +func (DeviceInfoSet DevInfo) BuildDriverInfoList(DeviceInfoData *SP_DEVINFO_DATA, DriverType SPDIT) (err error) { + return SetupDiBuildDriverInfoList(DeviceInfoSet, DeviceInfoData, DriverType) +} + +//sys SetupDiCancelDriverInfoSearch(DeviceInfoSet DevInfo) (err error) = setupapi.SetupDiCancelDriverInfoSearch + +// CancelDriverInfoSearch method cancels a driver list search that is currently in progress in a different thread. +func (DeviceInfoSet DevInfo) CancelDriverInfoSearch() (err error) { + return SetupDiCancelDriverInfoSearch(DeviceInfoSet) +} + +//sys setupDiEnumDriverInfo(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, DriverType SPDIT, MemberIndex uint32, DriverInfoData *SP_DRVINFO_DATA) (err error) = setupapi.SetupDiEnumDriverInfoW + +// SetupDiEnumDriverInfo function enumerates the members of a driver list. +func SetupDiEnumDriverInfo(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, DriverType SPDIT, MemberIndex int) (DriverInfoData *SP_DRVINFO_DATA, err error) { + data := &SP_DRVINFO_DATA{} + data.Size = uint32(unsafe.Sizeof(*data)) + + return data, setupDiEnumDriverInfo(DeviceInfoSet, DeviceInfoData, DriverType, uint32(MemberIndex), data) +} + +// EnumDriverInfo method enumerates the members of a driver list. +func (DeviceInfoSet DevInfo) EnumDriverInfo(DeviceInfoData *SP_DEVINFO_DATA, DriverType SPDIT, MemberIndex int) (DriverInfoData *SP_DRVINFO_DATA, err error) { + return SetupDiEnumDriverInfo(DeviceInfoSet, DeviceInfoData, DriverType, MemberIndex) +} + +//sys setupDiGetSelectedDriver(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, DriverInfoData *SP_DRVINFO_DATA) (err error) = setupapi.SetupDiGetSelectedDriverW + +// SetupDiGetSelectedDriver function retrieves the selected driver for a device information set or a particular device information element. +func SetupDiGetSelectedDriver(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA) (DriverInfoData *SP_DRVINFO_DATA, err error) { + data := &SP_DRVINFO_DATA{} + data.Size = uint32(unsafe.Sizeof(*data)) + + return data, setupDiGetSelectedDriver(DeviceInfoSet, DeviceInfoData, data) +} + +// GetSelectedDriver method retrieves the selected driver for a device information set or a particular device information element. +func (DeviceInfoSet DevInfo) GetSelectedDriver(DeviceInfoData *SP_DEVINFO_DATA) (DriverInfoData *SP_DRVINFO_DATA, err error) { + return SetupDiGetSelectedDriver(DeviceInfoSet, DeviceInfoData) +} + +//sys SetupDiSetSelectedDriver(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, DriverInfoData *SP_DRVINFO_DATA) (err error) = setupapi.SetupDiSetSelectedDriverW + +// SetSelectedDriver method sets, or resets, the selected driver for a device information element or the selected class driver for a device information set. +func (DeviceInfoSet DevInfo) SetSelectedDriver(DeviceInfoData *SP_DEVINFO_DATA, DriverInfoData *SP_DRVINFO_DATA) (err error) { + return SetupDiSetSelectedDriver(DeviceInfoSet, DeviceInfoData, DriverInfoData) +} + +//sys setupDiGetDriverInfoDetail(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, DriverInfoData *SP_DRVINFO_DATA, DriverInfoDetailData *_SP_DRVINFO_DETAIL_DATA, DriverInfoDetailDataSize uint32, RequiredSize *uint32) (err error) = setupapi.SetupDiGetDriverInfoDetailW + +// SetupDiGetDriverInfoDetail function retrieves driver information detail for a device information set or a particular device information element in the device information set. +func SetupDiGetDriverInfoDetail(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, DriverInfoData *SP_DRVINFO_DATA) (DriverInfoDetailData *DrvInfoDetailData, err error) { + const bufCapacity = 0x800 + buf := [bufCapacity]byte{} + var bufLen uint32 + + _data := (*_SP_DRVINFO_DETAIL_DATA)(unsafe.Pointer(&buf[0])) + _data.Size = uint32(unsafe.Sizeof(*_data)) + + err = setupDiGetDriverInfoDetail(DeviceInfoSet, DeviceInfoData, DriverInfoData, _data, bufCapacity, &bufLen) + if err == nil { + // The buffer was was sufficiently big. + return _data.toGo(bufLen), nil + } + + if errWin, ok := err.(syscall.Errno); ok && errWin == windows.ERROR_INSUFFICIENT_BUFFER { + // The buffer was too small. Now that we got the required size, create another one big enough and retry. + buf := make([]byte, bufLen) + _data := (*_SP_DRVINFO_DETAIL_DATA)(unsafe.Pointer(&buf[0])) + _data.Size = uint32(unsafe.Sizeof(*_data)) + + err = setupDiGetDriverInfoDetail(DeviceInfoSet, DeviceInfoData, DriverInfoData, _data, bufLen, &bufLen) + if err == nil { + return _data.toGo(bufLen), nil + } + } + + return +} + +// GetDriverInfoDetail method retrieves driver information detail for a device information set or a particular device information element in the device information set. +func (DeviceInfoSet DevInfo) GetDriverInfoDetail(DeviceInfoData *SP_DEVINFO_DATA, DriverInfoData *SP_DRVINFO_DATA) (DriverInfoDetailData *DrvInfoDetailData, err error) { + return SetupDiGetDriverInfoDetail(DeviceInfoSet, DeviceInfoData, DriverInfoData) +} + +//sys SetupDiDestroyDriverInfoList(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, DriverType SPDIT) (err error) = setupapi.SetupDiDestroyDriverInfoList + +// DestroyDriverInfoList method deletes a driver list. +func (DeviceInfoSet DevInfo) DestroyDriverInfoList(DeviceInfoData *SP_DEVINFO_DATA, DriverType SPDIT) (err error) { + return SetupDiDestroyDriverInfoList(DeviceInfoSet, DeviceInfoData, DriverType) +} + //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 // SetupDiGetClassDevsEx function returns a handle to a device information set that contains requested device information elements for a local or a remote computer. diff --git a/setupapi/setupapi_windows_test.go b/setupapi/setupapi_windows_test.go index 93ae42a..12b5fe1 100644 --- a/setupapi/setupapi_windows_test.go +++ b/setupapi/setupapi_windows_test.go @@ -136,6 +136,69 @@ func TestSetupDiEnumDeviceInfo(t *testing.T) { } } +func TestDevInfo_BuildDriverInfoList(t *testing.T) { + devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "", 0, DIGCF_PRESENT, DevInfo(0), "") + if err != nil { + t.Errorf("Error calling SetupDiGetClassDevsEx: %s", err.Error()) + } + defer devInfoList.Close() + + for i := 0; true; i++ { + deviceData, err := devInfoList.EnumDeviceInfo(i) + if err != nil { + if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ { + break + } + continue + } + + const driverType SPDIT = SPDIT_COMPATDRIVER + err = devInfoList.BuildDriverInfoList(deviceData, driverType) + if err != nil { + t.Errorf("Error calling SetupDiBuildDriverInfoList: %s", err.Error()) + } + defer devInfoList.DestroyDriverInfoList(deviceData, driverType) + + var selectedDriverData *SP_DRVINFO_DATA + for j := 0; true; j++ { + driverData, err := devInfoList.EnumDriverInfo(deviceData, driverType, j) + if err != nil { + if errWin, ok := err.(syscall.Errno); ok && errWin == 259 /*ERROR_NO_MORE_ITEMS*/ { + break + } + continue + } + + if driverData2, err2 := driverData.ToGo().ToWindows(); err2 != nil || *driverData2 != *driverData { + t.Error("Error converting between SP_DRVINFO_DATA and DrvInfoData") + } + + if driverData.DriverType == 0 { + continue + } + + err = devInfoList.SetSelectedDriver(deviceData, driverData) + if err != nil { + t.Errorf("Error calling SetupDiSetSelectedDriver: %s", err.Error()) + } else { + selectedDriverData = driverData + } + + _, err = devInfoList.GetDriverInfoDetail(deviceData, driverData) + if err != nil { + t.Errorf("Error calling SetupDiGetDriverInfoDetail: %s", err.Error()) + } + } + + selectedDriverData2, err := devInfoList.GetSelectedDriver(deviceData) + if err != nil { + t.Errorf("Error calling SetupDiGetSelectedDriver: %s", err.Error()) + } else if *selectedDriverData != *selectedDriverData2 { + t.Error("SetupDiGetSelectedDriver should return driver selected with SetupDiSetSelectedDriver") + } + } +} + func TestSetupDiGetClassDevsEx(t *testing.T) { devInfoList, err := SetupDiGetClassDevsEx(&deviceClassNetGUID, "PCI", 0, DIGCF_PRESENT, DevInfo(0), computerName) if err == nil { diff --git a/setupapi/types_windows.go b/setupapi/types_windows.go index 9bbe38b..0eb640c 100644 --- a/setupapi/types_windows.go +++ b/setupapi/types_windows.go @@ -23,6 +23,20 @@ const ( CONFIGMG_VERSION = 0x0400 ) +// +// Define maximum string length constants +// +const ( + LINE_LEN = 256 // Windows 9x-compatible maximum for displayable strings coming from a device INF. + MAX_INF_STRING_LENGTH = 4096 // Actual maximum size of an INF string (including string substitutions). + MAX_INF_SECTION_NAME_LENGTH = 255 // For Windows 9x compatibility, INF section names should be constrained to 32 characters. + MAX_TITLE_LEN = 60 + MAX_INSTRUCTION_LEN = 256 + MAX_LABEL_LEN = 30 + MAX_SERVICE_NAME_LEN = 256 + MAX_SUBTITLE_LEN = 256 +) + const ( // SP_MAX_MACHINENAME_LENGTH defines maximum length of a machine name in the format expected by ConfigMgr32 CM_Connect_Machine (i.e., "\\\\MachineName\0"). SP_MAX_MACHINENAME_LENGTH = windows.MAX_PATH + 3 @@ -285,6 +299,128 @@ const ( DICS_FLAG_CONFIGGENERAL DICS_FLAG = 0x00000004 // 1 or more hardware profile-specific changes to follow ) +type SP_DRVINFO_DATA struct { + Size uint32 + DriverType uint32 + _ uintptr + Description [LINE_LEN]uint16 + MfgName [LINE_LEN]uint16 + ProviderName [LINE_LEN]uint16 + DriverDate windows.Filetime + DriverVersion uint64 +} + +func (data SP_DRVINFO_DATA) ToGo() *DrvInfoData { + return &DrvInfoData{ + DriverType: data.DriverType, + Description: windows.UTF16ToString(data.Description[:]), + MfgName: windows.UTF16ToString(data.MfgName[:]), + ProviderName: windows.UTF16ToString(data.ProviderName[:]), + DriverDate: data.DriverDate, + DriverVersion: data.DriverVersion, + } +} + +// 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) +type DrvInfoData struct { + DriverType uint32 + Description string + MfgName string + ProviderName string + DriverDate windows.Filetime + DriverVersion uint64 +} + +func (DriverInfoData DrvInfoData) ToWindows() (data *SP_DRVINFO_DATA, err error) { + data = &SP_DRVINFO_DATA{ + DriverType: DriverInfoData.DriverType, + DriverDate: DriverInfoData.DriverDate, + DriverVersion: DriverInfoData.DriverVersion, + } + data.Size = uint32(unsafe.Sizeof(*data)) + + DescriptionUTF16, err := syscall.UTF16FromString(DriverInfoData.Description) + if err != nil { + return + } + copy(data.Description[:], DescriptionUTF16) + + MfgNameUTF16, err := syscall.UTF16FromString(DriverInfoData.MfgName) + if err != nil { + return + } + copy(data.MfgName[:], MfgNameUTF16) + + ProviderNameUTF16, err := syscall.UTF16FromString(DriverInfoData.ProviderName) + if err != nil { + return + } + copy(data.ProviderName[:], ProviderNameUTF16) + + return +} + +type _SP_DRVINFO_DETAIL_DATA struct { + Size uint32 + InfDate windows.Filetime + CompatIDsOffset uint32 + CompatIDsLength uint32 + _ uintptr + SectionName [LINE_LEN]uint16 + InfFileName [windows.MAX_PATH]uint16 + DrvDescription [LINE_LEN]uint16 + HardwareID [1]uint16 +} + +func (_data _SP_DRVINFO_DETAIL_DATA) toGo(bufLen uint32) (DriverInfoDetailData *DrvInfoDetailData) { + DriverInfoDetailData = &DrvInfoDetailData{ + InfDate: _data.InfDate, + SectionName: windows.UTF16ToString(_data.SectionName[:]), + InfFileName: windows.UTF16ToString(_data.InfFileName[:]), + DrvDescription: windows.UTF16ToString(_data.DrvDescription[:]), + CompatIDs: []string{}, + } + + bufW := _data.getBuf(bufLen) + + if _data.CompatIDsOffset > 1 { + DriverInfoDetailData.HardwareID = windows.UTF16ToString(bufW[:wcslen(bufW)]) + } + + if _data.CompatIDsLength > 0 { + bufW = bufW[_data.CompatIDsOffset : _data.CompatIDsOffset+_data.CompatIDsLength] + for i := 0; i < len(bufW); { + j := i + wcslen(bufW[i:]) + if i < j { + DriverInfoDetailData.CompatIDs = append(DriverInfoDetailData.CompatIDs, windows.UTF16ToString(bufW[i:j])) + } + i = j + 1 + } + } + + return +} + +func (_data _SP_DRVINFO_DETAIL_DATA) getBuf(bufLen uint32) []uint16 { + len := (bufLen - uint32(unsafe.Offsetof(_data.HardwareID))) / 2 + sl := struct { + addr *uint16 + len int + cap int + }{&_data.HardwareID[0], int(len), int(len)} + return *(*[]uint16)(unsafe.Pointer(&sl)) +} + +// DrvInfoDetailData is driver information details structure (provides detailed information about a particular driver information structure) +type DrvInfoDetailData struct { + InfDate windows.Filetime + SectionName string + InfFileName string + DrvDescription string + HardwareID string + CompatIDs []string +} + // DICD flags control SetupDiCreateDeviceInfo type DICD uint32 @@ -293,6 +429,19 @@ const ( DICD_INHERIT_CLASSDRVS DICD = 0x00000002 ) +// +// SPDIT flags to distinguish between class drivers and +// device drivers. +// (Passed in 'DriverType' parameter of driver information list APIs) +// +type SPDIT uint32 + +const ( + SPDIT_NODRIVER SPDIT = 0x00000000 + SPDIT_CLASSDRIVER SPDIT = 0x00000001 + SPDIT_COMPATDRIVER SPDIT = 0x00000002 +) + // DIGCF flags control what is included in the device information set built by SetupDiGetClassDevs type DIGCF uint32 diff --git a/setupapi/zsetupapi_windows.go b/setupapi/zsetupapi_windows.go index c9aa72a..7e681f5 100644 --- a/setupapi/zsetupapi_windows.go +++ b/setupapi/zsetupapi_windows.go @@ -44,6 +44,13 @@ var ( procSetupDiCreateDeviceInfoW = modsetupapi.NewProc("SetupDiCreateDeviceInfoW") procSetupDiEnumDeviceInfo = modsetupapi.NewProc("SetupDiEnumDeviceInfo") procSetupDiDestroyDeviceInfoList = modsetupapi.NewProc("SetupDiDestroyDeviceInfoList") + procSetupDiBuildDriverInfoList = modsetupapi.NewProc("SetupDiBuildDriverInfoList") + procSetupDiCancelDriverInfoSearch = modsetupapi.NewProc("SetupDiCancelDriverInfoSearch") + procSetupDiEnumDriverInfoW = modsetupapi.NewProc("SetupDiEnumDriverInfoW") + procSetupDiGetSelectedDriverW = modsetupapi.NewProc("SetupDiGetSelectedDriverW") + procSetupDiSetSelectedDriverW = modsetupapi.NewProc("SetupDiSetSelectedDriverW") + procSetupDiGetDriverInfoDetailW = modsetupapi.NewProc("SetupDiGetDriverInfoDetailW") + procSetupDiDestroyDriverInfoList = modsetupapi.NewProc("SetupDiDestroyDriverInfoList") procSetupDiGetClassDevsExW = modsetupapi.NewProc("SetupDiGetClassDevsExW") procSetupDiCallClassInstaller = modsetupapi.NewProc("SetupDiCallClassInstaller") procSetupDiOpenDevRegKey = modsetupapi.NewProc("SetupDiOpenDevRegKey") @@ -120,6 +127,90 @@ func SetupDiDestroyDeviceInfoList(DeviceInfoSet DevInfo) (err error) { return } +func SetupDiBuildDriverInfoList(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, DriverType SPDIT) (err error) { + r1, _, e1 := syscall.Syscall(procSetupDiBuildDriverInfoList.Addr(), 3, uintptr(DeviceInfoSet), uintptr(unsafe.Pointer(DeviceInfoData)), uintptr(DriverType)) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func SetupDiCancelDriverInfoSearch(DeviceInfoSet DevInfo) (err error) { + r1, _, e1 := syscall.Syscall(procSetupDiCancelDriverInfoSearch.Addr(), 1, uintptr(DeviceInfoSet), 0, 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setupDiEnumDriverInfo(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, DriverType SPDIT, MemberIndex uint32, DriverInfoData *SP_DRVINFO_DATA) (err error) { + r1, _, e1 := syscall.Syscall6(procSetupDiEnumDriverInfoW.Addr(), 5, uintptr(DeviceInfoSet), uintptr(unsafe.Pointer(DeviceInfoData)), uintptr(DriverType), uintptr(MemberIndex), uintptr(unsafe.Pointer(DriverInfoData)), 0) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setupDiGetSelectedDriver(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, DriverInfoData *SP_DRVINFO_DATA) (err error) { + r1, _, e1 := syscall.Syscall(procSetupDiGetSelectedDriverW.Addr(), 3, uintptr(DeviceInfoSet), uintptr(unsafe.Pointer(DeviceInfoData)), uintptr(unsafe.Pointer(DriverInfoData))) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func SetupDiSetSelectedDriver(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, DriverInfoData *SP_DRVINFO_DATA) (err error) { + r1, _, e1 := syscall.Syscall(procSetupDiSetSelectedDriverW.Addr(), 3, uintptr(DeviceInfoSet), uintptr(unsafe.Pointer(DeviceInfoData)), uintptr(unsafe.Pointer(DriverInfoData))) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func setupDiGetDriverInfoDetail(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, DriverInfoData *SP_DRVINFO_DATA, DriverInfoDetailData *_SP_DRVINFO_DETAIL_DATA, DriverInfoDetailDataSize uint32, RequiredSize *uint32) (err error) { + r1, _, e1 := syscall.Syscall6(procSetupDiGetDriverInfoDetailW.Addr(), 6, uintptr(DeviceInfoSet), uintptr(unsafe.Pointer(DeviceInfoData)), uintptr(unsafe.Pointer(DriverInfoData)), uintptr(unsafe.Pointer(DriverInfoDetailData)), uintptr(DriverInfoDetailDataSize), uintptr(unsafe.Pointer(RequiredSize))) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + +func SetupDiDestroyDriverInfoList(DeviceInfoSet DevInfo, DeviceInfoData *SP_DEVINFO_DATA, DriverType SPDIT) (err error) { + r1, _, e1 := syscall.Syscall(procSetupDiDestroyDriverInfoList.Addr(), 3, uintptr(DeviceInfoSet), uintptr(unsafe.Pointer(DeviceInfoData)), uintptr(DriverType)) + if r1 == 0 { + if e1 != 0 { + err = errnoErr(e1) + } else { + err = syscall.EINVAL + } + } + return +} + func setupDiGetClassDevsEx(ClassGUID *windows.GUID, Enumerator *uint16, hwndParent uintptr, Flags DIGCF, DeviceInfoSet DevInfo, MachineName *uint16, reserved uintptr) (handle DevInfo, err error) { r0, _, e1 := syscall.Syscall9(procSetupDiGetClassDevsExW.Addr(), 7, uintptr(unsafe.Pointer(ClassGUID)), uintptr(unsafe.Pointer(Enumerator)), uintptr(hwndParent), uintptr(Flags), uintptr(DeviceInfoSet), uintptr(unsafe.Pointer(MachineName)), uintptr(reserved), 0, 0) handle = DevInfo(r0)