通過USB VID和PID卸載USB設備

以前也發過一幾篇關於卸載USB設備的文章,其實原理都是一樣都是使用同一個API "CM_Request_Device_Eject_ExW"來完成卸載工作,上一篇是通過遍歷USB接點實現,這篇文章直接通過USB 的VID和PID獲取其對應的DevInst來完成卸載工作。本篇文章通過使用了WMI技術來實現了很多關鍵點的功能。

 

查了很多資料想了很多方法也沒找到從一個USB盤符獲取該USB設備的VID和PID串比如,我的U盤VID和PID串是“Vid_0781&Pid_5151”,SerialNumber是“2204611D84C38930”,那麼我們就可以通過

CM_Locate_DevNodeA(VarPtr(dwDevInst), "USB/Vid_0781&Pid_5151/2204611D84C38930", 0)來獲取到dwDevInst,這樣繼續使用CM_Request_Device_Eject_ExA函數就可以完成對USB設備的卸載工作了。如果哪位朋友知道怎麼從U盤的盤符獲取到VID和PID串請與我聯繫,謝謝!

 

form

 

Option Explicit
Private Declare Function CM_Locate_DevNodeA Lib "setupapi.dll" (ByVal pdnDevInst As Long, ByVal DeviceInstanceId As String, ByVal ulFlags As Long) As Long
Private Declare Function CM_Request_Device_Eject_ExA Lib "setupapi.dll" (ByVal pdnDevInst As Long, ByVal VetoType As Long, ByVal pszVetoName As String, ByVal ulNameLength As Long, ByVal ulFlags As Long, ByVal hMachine As Long) As Long
'BOOL WINAPI CM_Locate_DevNodeA ( OUT PDEVINST pdnDevInst, IN DEVINSTID_A pDeviceID, IN ULONG ulFlags )
'BOOL WINAPI CM_Request_Device_Eject_ExA ( IN DEVINST dnDevInst, OUT PPNP_VETO_TYPE pVetoType, IN LPSTR pszVetoName, IN ULONG ulNameLength, IN ULONG ulFlags, IN HMACHINE hMachine )

Private Function GetSerialNumber(ByVal strDeviceId As String) As String '在DeviceId中獲取SerialNumber
    Dim i As Integer
    Dim Length As Integer
    Length = Len(strDeviceId)
    For i = Length To 1 Step -1
        If Mid(strDeviceId, i, 1) = "/" Then
            GetSerialNumber = Mid(strDeviceId, i + 1, Length - i - 1)
            Exit Function
        End If
    Next
End Function

Private Sub GetUsbDevices() '獲取所有usb分區
    Dim strWQL As String
    Dim objSWbemServices As SWbemServices, objWmiObject As SWbemObject, objWmiObjectSet As SWbemObjectSet, objPattions As SWbemObjectSet, objPattion As SWbemObject
    Dim objLogicalDiskToPartitions As SWbemObjectSet, objLogicalDiskToPartition As SWbemObject
   
    If ConnectWmiServer(objSWbemServices, ".") Then
        strWQL = "Select * From Win32_DiskDrive where InterfaceType='USB'"
        Set objPattions = objSWbemServices.ExecQuery(strWQL)
        For Each objPattion In objPattions
            strWQL = "Associators of {win32_DiskDrive.DeviceID='" & objPattion.DeviceID & "'} where AssocClass = Win32_DiskDriveToDiskPartition"
            Set objWmiObjectSet = objSWbemServices.ExecQuery(strWQL)
            For Each objWmiObject In objWmiObjectSet
                Debug.Print objWmiObject.Description; objWmiObject.Name; objWmiObject.PNPDeviceID; objWmiObject.Index
                strWQL = "Associators of {Win32_DiskPartition.DeviceID='" & objWmiObject.DeviceID & "'} where AssocClass = Win32_LogicalDiskToPartition"
                Set objLogicalDiskToPartitions = objSWbemServices.ExecQuery(strWQL)
                For Each objLogicalDiskToPartition In objLogicalDiskToPartitions
                    cboUsbDriveList.AddItem "<Disk:" & objPattion.Index & ":" & objWmiObject.Index + 1 & ">" & objLogicalDiskToPartition.Description & "(" & objLogicalDiskToPartition.Name & ")"
                Next
            Next
        Next
        If cboUsbDriveList.ListCount Then
            cboUsbDriveList.ListIndex = 0
        Else
            cboUsbDriveList.Text = "目前沒有發現USB設備"
        End If
        Set objSWbemServices = Nothing
        Set objWmiObject = Nothing
        Set objWmiObjectSet = Nothing
        Set objPattions = Nothing
        Set objPattion = Nothing
        Set objLogicalDiskToPartitions = Nothing
        Set objLogicalDiskToPartition = Nothing
    End If
End Sub

Private Sub cmdExit_Click()
    Unload Me
End Sub

Private Sub cmdRefresh_Click()
    Me.cboUsbDriveList.Clear
    GetUsbDevices
End Sub

Private Sub cmdUnload_Click()
    Dim strUsbHubs() As String
    Dim KeyCount As Long, SerialNumberCount As Long
    Dim i As Long, j As Long
    Dim strSerialNumbers() As String
    Dim strDrive As String
    Dim strWQL As String
    Dim objSWbemServices As SWbemServices, objWmiObject As SWbemObject, objWmiObjectSet As SWbemObjectSet
    Dim objPattions As SWbemObjectSet, objPattion As SWbemObject
    Dim strDeviceId As String
    Dim strDeviceInstanceId As String
    Dim strSerialNumber As String
    Dim lngRet As Long, dwDevInst As Long
   
    On Error GoTo ErrorHandle
    strDrive = Mid(cboUsbDriveList.List(cboUsbDriveList.ListIndex), InStr(cboUsbDriveList.List(cboUsbDriveList.ListIndex), "(") + 1, 2)
    strWQL = "Associators of {Win32_LogicalDisk='" & strDrive & "'} where ResultClass = Win32_DiskPartition"
    If ConnectWmiServer(objSWbemServices, ".") Then
        '這裏獲取了所有磁盤的DeviceId這裏麪包括了磁盤的SerialNumber,下面我們需要用SerialNumber去查找USB的VID和PID
        Set objPattions = objSWbemServices.ExecQuery(strWQL)
        For Each objPattion In objPattions
            strWQL = "Select * From win32_DiskDrive where Index=" & objPattion.DiskIndex
            Set objWmiObjectSet = objSWbemServices.ExecQuery(strWQL)
            For Each objWmiObject In objWmiObjectSet
                strDeviceId = objWmiObject.PNPDeviceID
                strSerialNumber = GetSerialNumber(strDeviceId)
                If InStr(strSerialNumber, "&") Then
                    strSerialNumber = Left(strSerialNumber, InStr(strSerialNumber, "&") - 1)
                End If
                '遍歷所有USB設備,這裏包括正在使用的和曾經使用過的我們通過SerialNumber去查找目前正在使用的
                strUsbHubs = GetSubKeys("/Registry/Machine/SYSTEM/CurrentControlSet/Enum/USB")
                KeyCount = UBound(strUsbHubs) + 1
                For i = 0 To KeyCount - 1
                    '查找所有USB設備的SerialNumber
                    strSerialNumbers = GetSubKeys("/Registry/Machine/SYSTEM/CurrentControlSet/Enum/USB/" & strUsbHubs(i))
                    SerialNumberCount = UBound(strSerialNumbers) + 1
                    For j = 0 To SerialNumberCount - 1
                        If strSerialNumber = strSerialNumbers(j) Then
                            strDeviceInstanceId = "USB/" & strUsbHubs(i) & "/" & strSerialNumbers(j)
                            '這裏這種方法不是很好,是通過U盤的SerialNumber去查找VID和PID對於一些沒有SerialNumber的顯然這種方法是不行的
                            '目前我還沒想到怎麼直接從U盤的盤符取到VID和PID現在只有將就用這種方法了
                            lngRet = CM_Locate_DevNodeA(VarPtr(dwDevInst), strDeviceInstanceId, 0)
                            If lngRet = 0 Then
                                lngRet = CM_Request_Device_Eject_ExA(dwDevInst, 0, vbNullString, 0, 0, 0)
                                Exit For
                            End If
                        End If
                    Next
                    Erase strSerialNumbers
                Next
                Erase strUsbHubs
            Next
        Next
    End If

ErrorHandle:
    Set objSWbemServices = Nothing
    Set objWmiObject = Nothing
    Set objWmiObjectSet = Nothing
    Set objPattions = Nothing
    Set objPattion = Nothing
    cmdRefresh_Click
End Sub

Private Sub Form_Load()
    GetUsbDevices
End Sub

 

'連接WMI服務函數(此函數也可以連接遠程計算機,當要連接遠程計算機時把參數“strComputerName”指示爲IP地址即可但是注意的是還要提供用戶名和密碼)
Private Function ConnectWmiServer(ByRef objSWbemServices As SWbemServices, _
                                  ByVal strComputerName As String, _
                                  Optional ByVal strNameSpace As String = "root/cimv2", _
                                  Optional ByVal strUserName As String = "", _
                                  Optional ByVal strPassWord As String = "" _
                                  ) As Boolean
    Dim objSWbemLocator As SWbemLocator
    On Error GoTo ErrLine
    Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator")
    '提升權限爲DEBUG權限
    objSWbemLocator.Security_.Privileges.Add wbemPrivilegeDebug
    If strComputerName <> "." Then
        Set objSWbemServices = objSWbemLocator.ConnectServer(strComputerName, strNameSpace, strUserName, strPassWord)
    Else
        Set objSWbemServices = objSWbemLocator.ConnectServer()
    End If
    ConnectWmiServer = True
    Set objSWbemLocator = Nothing
    Exit Function
ErrLine:
    ConnectWmiServer = False
    Set objSWbemLocator = Nothing
End Function

 

bas:

 

Option Explicit

Private Type UNICODE_STRING
    Length As Integer
    MaximumLength As Integer
    Buffer As Long
End Type

Private Type OBJECT_ATTRIBUTES
    Length As Long
    RootDirectory As Long
    ObjectName As Long
    Attributes As Long
    SecurityDescriptor As Long
    SecurityQualityOfService As Long
End Type

Private Type KEY_VALUE_FULL_INFORMATION
    TitleIndex As Long
    Type As Long
    DataOffset As Long
    DataLength As Long
    NameLength As Long
    Name As Long
End Type

Private Type LARGE_INTEGER
    Lowpart As Long
    Highpart As Long
End Type

Private Type KEY_BASIC_INFORMATION
    LastWriteTim As LARGE_INTEGER
    TitleIndex As Long
    NameLength As Long
    Name As Long
End Type

Private Type KEY_FULL_INFORMATION
    LastWriteTim As LARGE_INTEGER
    TitleIndex As Long
    ClassOffset As Long
    ClassLength As Long
    SubKeys As Long
    MaxNameLen As Long
    MaxClassLen As Long
    Values As Long
    MaxValueNameLen As Long
    MaxValueDataLen As Long
    Class As Long
End Type

Private Enum KEY_INFORMATION_CLASS
    KeyBasicInformation
    KeyNodeInformation
    KeyFullInformation
    KeyNameInformation
    KeyCachedInformation
    KeyFlagsInformation
End Enum

Private Enum KEY_VALUE_INFORMATION_CLASS
    KeyValueBasicInformation
    KeyValueFullInformation
    KeyValuePartialInformation
    KeyValueFullInformationAlign64
    KeyValuePartialInformationAlign64
End Enum

Private Const STATUS_BUFFER_OVERFLOW = &H80000005
Private Const STATUS_BUFFER_TOO_SMALL = &HC0000023
Private Const OBJ_CASE_INSENSITIVE = &H40

Private Const READ_CONTROL = &H20000
Private Const KEY_QUERY_VALUE = &H1
Private Const KEY_ENUMERATE_SUB_KEYS = &H8
Private Const KEY_NOTIFY = &H10
Private Const KEY_READ = KEY_QUERY_VALUE + KEY_ENUMERATE_SUB_KEYS + KEY_NOTIFY + READ_CONTROL

Private Declare Function ZwClose Lib "ntdll.dll" (ByVal ObjectHandle As Long) As Long

Private Declare Sub RtlInitUnicodeString Lib "ntdll.dll" (ByVal DestinationString As Long, ByVal SourceString As Long)

Private Declare Function ZwOpenKey Lib "ntdll.dll" (KeyHandle As Long, ByVal DesiredAccess As Long, ByVal ObjectAttributes As Long) As Long

Private Declare Function ZwQueryKey Lib "ntdll.dll" (ByVal KeyHandle As Long, _
                                                     ByVal KeyInformationClass As KEY_INFORMATION_CLASS, _
                                                     ByVal KeyInformation As Long, _
                                                     ByVal KeyInformationLength As Long, _
                                                     ResultLength As Long _
                                                     ) As Long
                                                    
Private Declare Function ZwEnumerateValueKey Lib "ntdll.dll" (ByVal KeyHandle As Long, _
                                                              ByVal Index As Long, _
                                                              ByVal KeyValueInformationClass As KEY_VALUE_INFORMATION_CLASS, _
                                                              ByVal KeyValueInformation As Long, _
                                                              ByVal KeyValueInformationLength As Long, _
                                                              ResultLength As Long _
                                                              ) As Long
                                                             
Private Declare Function ZwEnumerateKey Lib "ntdll.dll" (ByVal KeyHandle As Long, _
                                                         ByVal Index As Long, _
                                                         ByVal KeyInformationClass As KEY_INFORMATION_CLASS, _
                                                         ByVal KeyInformation As Long, _
                                                         ByVal KeyInformationLength As Long, _
                                                         ResultLength As Long _
                                                         ) As Long
                                                             
                                                             
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long, ByVal Length As Long)

Public Function GetSubKeys(ByVal lpRegKey As String) As String()
    Dim strSubKeys() As String
    Dim KeyHandle As Long
    Dim ntStatus As Long
    Dim ResultLength As Long
    Dim bytBuffer() As Byte
    Dim bytValueBuffer() As Byte
    Dim strValue As String
    Dim KeyBase As KEY_BASIC_INFORMATION
    Dim KeyValueFull As KEY_VALUE_FULL_INFORMATION
    Dim KeyFull As KEY_FULL_INFORMATION
    Dim i As Integer
    Dim ustrKeyName As UNICODE_STRING
    Dim objAttr As OBJECT_ATTRIBUTES
    Dim strKeyName As String
   
    RtlInitUnicodeString VarPtr(ustrKeyName), StrPtr(lpRegKey)
   
    objAttr.Length = LenB(objAttr)
    objAttr.ObjectName = VarPtr(ustrKeyName)
    objAttr.Attributes = OBJ_CASE_INSENSITIVE
    ntStatus = ZwOpenKey(KeyHandle, KEY_READ, VarPtr(objAttr))
    If ntStatus >= 0 Then
        ntStatus = ZwQueryKey(KeyHandle, _
                              KeyFullInformation, _
                              0, _
                              0, _
                              ResultLength _
                              )
        If ntStatus = STATUS_BUFFER_OVERFLOW Or ntStatus = STATUS_BUFFER_TOO_SMALL Then
            ReDim bytBuffer(ResultLength - 1)
            ntStatus = ZwQueryKey(KeyHandle, _
                                  KeyFullInformation, _
                                  VarPtr(bytBuffer(0)), _
                                  ResultLength, _
                                  ResultLength _
                                  )
            If ntStatus >= 0 Then
                CopyMemory VarPtr(KeyFull), VarPtr(bytBuffer(0)), LenB(KeyFull)
                ReDim strSubKeys(KeyFull.SubKeys - 1)
                For i = 0 To KeyFull.SubKeys - 1
                    ntStatus = ZwEnumerateKey(KeyHandle, _
                                              i, _
                                              KeyBasicInformation, _
                                              0, _
                                              0, _
                                              ResultLength _
                                              )
                    If ntStatus = STATUS_BUFFER_OVERFLOW Or ntStatus = STATUS_BUFFER_TOO_SMALL Then
                        ReDim bytValueBuffer(ResultLength - 1)
                        ntStatus = ZwEnumerateKey(KeyHandle, _
                                                  i, _
                                                  KeyBasicInformation, _
                                                  VarPtr(bytValueBuffer(0)), _
                                                  ResultLength, _
                                                  ResultLength _
                                                  )
                        If ntStatus >= 0 Then
                            CopyMemory VarPtr(KeyBase), VarPtr(bytValueBuffer(0)), LenB(KeyBase)
                            strValue = String(KeyBase.NameLength / 2, 0)
                            CopyMemory StrPtr(strValue), VarPtr(bytValueBuffer(0)) + 16, KeyBase.NameLength
                            strKeyName = strValue
                            strSubKeys(i) = strKeyName
                        End If
                        Erase bytValueBuffer
                    End If
                Next
            End If
            Erase bytBuffer
        End If
        ZwClose KeyHandle
    End If
    GetSubKeys = strSubKeys
End Function

                                                    

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章