[Solved] About using 'setupapi.dll'

Just starting out? Need help? Post your questions and find answers here.
ZX80
Enthusiast
Enthusiast
Posts: 365
Joined: Mon Dec 12, 2016 1:37 pm

[Solved] About using 'setupapi.dll'

Post by ZX80 »

Hello!

I ran into a problem getting detailed device information. I took the AAT code as a basis and slightly modified it to fit my needs. I also looked at the code section of interest from another member of our forum (which has the function SetupDiGetDeviceInterfaceDetail working). I tried to combine them but failed. I left comments in the code so you can understand what I want to do.

Code: Select all

Prototype SetupDiGetDeviceRegistryProperty(a, b, c, d, e, f, g)
Prototype SetupDiEnumDeviceInterfaces(a, b, c, d, e)
Prototype SetupDiGetDeviceInterfaceDetail(a, b, c, d, e, f)

Define setupapi
setupapi = OpenLibrary(#PB_Any, "setupapi.dll")
If setupapi
  SetupDiGetDeviceRegistryProperty.SetupDiGetDeviceRegistryProperty = GetFunction(setupapi, "SetupDiGetDeviceRegistryPropertyW")
  SetupDiEnumDeviceInterfaces.SetupDiEnumDeviceInterfaces = GetFunction(setupapi, "SetupDiEnumDeviceInterfaces")
  SetupDiGetDeviceInterfaceDetail.SetupDiGetDeviceInterfaceDetail = GetFunction(setupapi, "SetupDiGetDeviceInterfaceDetailW")
Else
  MessageRequester("Error", "Can't open library setupapi.dll")
  End  
EndIf

Structure SP_DEVICE_INTERFACE_DETAIL_DATA
  cbSize.l
  DevicePath.c[0]
EndStructure


Define hDevInfo
Define *pGUID = ?GUID_USB_DEVICE
hDevInfo = SetupDiGetClassDevs_(*pGUID, #Null, #Null, #DIGCF_PRESENT | #DIGCF_ALLCLASSES) 
If hDevInfo = #INVALID_HANDLE_VALUE    
  MessageRequester("Error", "Inavlid handle value")
  End
EndIf      

#MAX_CLASS_NAME_LEN = 32
#MAX_DEV_NAME_LEN   = 512

#SPDRP_HARDWAREID   = 1
#SPDRP_SERVICE      = 4

Define Index.i, cbRequired.l
Define.s DevName = Space(#MAX_DEV_NAME_LEN), DevClass = Space(#MAX_CLASS_NAME_LEN)
Define DeviceInfoData.SP_DEVICE_INTERFACE_DATA
DeviceInfoData\cbSize = SizeOf(SP_DEVICE_INTERFACE_DATA)

Define *pdidd.SP_DEVICE_INTERFACE_DETAIL_DATA


While SetupDiEnumDeviceInfo_(hDevInfo, Index, @DeviceInfoData)
  SetupDiGetDeviceRegistryProperty(hDevInfo, @DeviceInfoData, #SPDRP_SERVICE, #Null, @DevClass, #MAX_CLASS_NAME_LEN, #Null)              
  
  If DevClass = "USBSTOR"
    SetupDiGetDeviceRegistryProperty(hDevInfo, @DeviceInfoData, #SPDRP_HARDWAREID, #Null, @DevName, #MAX_DEV_NAME_LEN, #Null)    
    Debug "Device Name: " + DevName

    ;It looks like SetupDiGetDeviceInterfaceDetail should come after SetupDiEnumDeviceInterfaces.
    ;But I already have a SetupDiEnumDeviceInfo enum loop.
    ;How can I get the required size for the SP_DEVICE_INTERFACE DETAIL DATA structure, for getting more detail info about device ???
    ;I always get zero  :(
    cbRequired = 0
    If SetupDiEnumDeviceInterfaces(hDevInfo, #Null, *pGUID, Index, @DeviceInfoData)
      SetupDiGetDeviceInterfaceDetail(hDevInfo, @DeviceInfoData, #Null, #Null, @cbRequired, #Null)
    Else
      SetupDiGetDeviceInterfaceDetail(hDevInfo, @DeviceInfoData, #Null, #Null, @cbRequired, #Null)
    EndIf
    Debug "cbRequired = " + Str(cbRequired)

    ;Err = GetLastError_()
    ;If Err = #ERROR_INSUFFICIENT_BUFFER
        
    ;*pdidd = LocalAlloc_(#LPTR, cbRequired)
    ;*pdidd\cbSize=SizeOf(SP_DEVICE_INTERFACE_DETAIL_DATA)+SizeOf(character)
    ;If *pdidd
      ;some code
    ;  LocalFree_(*pdidd)
    ;  *pdidd=0
    ;EndIf

  EndIf    
  Index + 1
Wend 

SetupDiDestroyDeviceInfoList_(hDeviceInfoSet)
CloseLibrary(setupapi)


DataSection
  GUID_USB_DEVICE:
    Data.l $53f56307
    Data.w $b6bf, $11d0
    Data.b $94, $f2, $00, $a0, $c9, $1e, $fb, $8b
EndDataSection
I also saw other codes on this topic and everywhere the SetupDiGetDeviceInterfaceDetail function came after the enumeration of the interfaces. In this case it worked. But I also want to get the VID and PID of the device, which requires using the SetupDiGetDeviceRegistryProperty function. I was not able to combine these two codes together.
Does anyone have any ideas?

added:
I will also need 'devinst' for further action. To get it, you need to call the "CM_Locate_DevNodeW" function and to use this feature you must know the HARDWAREID. Everything is interconnected.

P.S. To be honest, I'm a bit confused.

P.S.2 Yes, you guessed it, I aimed for this:
PeekS(@*pdidd\DevicePath)
Last edited by ZX80 on Sat Mar 12, 2022 9:28 am, edited 1 time in total.
breeze4me
Enthusiast
Enthusiast
Posts: 633
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: About using 'setupapi.dll'

Post by breeze4me »

Try this.

Code: Select all

#CR_SUCCESS = 0
#CM_LOCATE_DEVNODE_NORMAL = 0

#MAX_CLASS_NAME_LEN = 32
#MAX_DEV_NAME_LEN   = 512

#SPDRP_HARDWAREID   = 1
#SPDRP_SERVICE      = 4

#SPDRP_FRIENDLYNAME = $0C
#SPDRP_ENUMERATOR_NAME = $16
#SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = $0E

Structure SP_DEVICE_INTERFACE_DETAIL_DATA
  cbSize.l
  DevicePath.s{255}
EndStructure

Structure SP_DEVINFO_DATA
  cbSize.l 
  ClassGuid.GUID 
  DevInst.l 
  *reserved
EndStructure 


Prototype ptCM_Get_Device_IDW(DevInst.l, *DeviceIdString, BufferLen.l, Flags.l)
Prototype ptCM_Get_Parent(*DevInst, DevInst.l, Flags)
Prototype ptCM_Locate_DevNodeW(*DevInst, *pDeviceID, ulFlags.l)

Global CM_Locate_DevNode.ptCM_Locate_DevNodeW
Global CM_Get_Parent.ptCM_Get_Parent
Global CM_Get_Device_ID.ptCM_Get_Device_IDW

Define setupapi

setupapi = OpenLibrary(#PB_Any, "setupapi.dll")
If setupapi
  CM_Get_Device_ID = GetFunction(setupapi, "CM_Get_Device_IDW")
  CM_Get_Parent = GetFunction(setupapi, "CM_Get_Parent")
  CM_Locate_DevNode = GetFunction(setupapi, "CM_Locate_DevNodeW")
Else
  MessageRequester("Error", "Can't open library setupapi.dll")
  End
EndIf

Define Index.i, cbRequired.l
Define DeviceInfoData.SP_DEVICE_INTERFACE_DATA

Define pdidd.SP_DEVICE_INTERFACE_DETAIL_DATA
Define spdd.SP_DEVINFO_DATA

Define hDevInfo
Define *pGUID = ?GUID_USB_DEVICE

Define Buffer.s{512}

DeviceInfoData\cbSize = SizeOf(SP_DEVICE_INTERFACE_DATA)


hDevInfo = SetupDiGetClassDevs_(*pGUID, #Null, #Null, #DIGCF_PRESENT | #DIGCF_DEVICEINTERFACE)
If hDevInfo = #INVALID_HANDLE_VALUE
  MessageRequester("Error", "Inavlid handle value")
  End
EndIf

While SetupDiEnumDeviceInterfaces_(hDevInfo, 0, *pGUID, Index, @DeviceInfoData)
  
  SetupDiGetDeviceInterfaceDetail_(hDevInfo, @DeviceInfoData, 0, 0, @cbRequired, 0)
  If cbRequired > 0 And cbRequired < 255
    
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
      pdidd\cbSize = 8      ; x86(Unicode=6, ASCII=5), x64=8
    CompilerElse
      pdidd\cbSize = 4 + SizeOf(Character)   ; x86(Unicode=6, ASCII=5), x64=8
    CompilerEndIf
    
    spdd\cbSize = SizeOf(SP_DEVINFO_DATA)
    
    If SetupDiGetDeviceInterfaceDetail_(hDevInfo, @DeviceInfoData, @pdidd, cbRequired, 0, @spdd)
      
      If FindString(pdidd\DevicePath, "USBSTOR", 1, #PB_String_NoCase)
        
        CM_Get_Parent(@DevInstParent, spdd\DevInst, 0)
        Debug "Parent DevInst: " + DevInstParent
        
        CM_Get_Device_ID(DevInstParent, @Buffer, 512, 0)
        Debug "Parent DeviceId: " + Buffer
        
        Debug ""
        
        Debug "DevicePath: " + pdidd\DevicePath
        Debug "DevInst: " + spdd\DevInst
        
        CM_Get_Device_ID(spdd\DevInst, @Buffer, 512, 0)
        Debug "DeviceId: " + Buffer
        
        CM_Locate_DevNode(@id, @Buffer, #CM_LOCATE_DEVNODE_NORMAL)
        Debug "DevInst: " + id
        
        SetupDiGetDeviceRegistryProperty_(hDevInfo, @spdd, #SPDRP_FRIENDLYNAME, 0, @Buffer, 512, 0)
        Debug "Friendly name: " + Buffer
        
        SetupDiGetDeviceRegistryProperty_(hDevInfo, @spdd, #SPDRP_ENUMERATOR_NAME, 0, @Buffer, 512, 0)
        Debug "SPDRP_ENUMERATOR_NAME: " + Buffer
        
        SetupDiGetDeviceRegistryProperty_(hDevInfo, @spdd, #SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, 0, @Buffer, 512, 0)
        Debug "SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: " + Buffer
        
        SetupDiGetDeviceRegistryProperty_(hDevInfo, @spdd, #SPDRP_HARDWAREID, 0, @Buffer, 512, 0)
        Debug "SPDRP_HARDWAREID: "
        Debug Buffer
        
        StrLength = StringByteLength(Buffer)
        *p = @Buffer + StrLength + 2
        While PeekC(*p)
          s.s = PeekS(*p)
          Debug s
          StrLength = StringByteLength(s)
          *p + StrLength + 2
        Wend
        
        Debug "----------------------------------------"
        
      EndIf
    EndIf
  EndIf
  
  Index + 1
Wend

SetupDiDestroyDeviceInfoList_(hDevInfo)
CloseLibrary(setupapi)


DataSection
  GUID_USB_DEVICE:
  Data.l $53f56307
  Data.w $b6bf, $11d0
  Data.b $94, $f2, $00, $a0, $c9, $1e, $fb, $8b
EndDataSection
ZX80
Enthusiast
Enthusiast
Posts: 365
Joined: Mon Dec 12, 2016 1:37 pm

Re: About using 'setupapi.dll'

Post by ZX80 »

Good time, breeze4me.

Thank you very much for your reply. Yes, that's what I need / looking for.
As I understand my mistake was here:

Code: Select all

pdidd\cbSize = 4 + SizeOf(Character)
And also in the structure used.

But for some reason it is not possible to control the equipment. Please, see if the prototype is written correctly. Although in fact we need only first parameter.

Code: Select all

Prototype ptCM_Request_Device_Eject_ExW(DevInst.l, *VetoType, VetoName.s, NameLength.l, Flags.l, hMachine)
...
Global CM_Request_Device_Eject.ptCM_Request_Device_Eject_ExW
...
CM_Request_Device_Eject = GetFunction(setupapi, "CM_Request_Device_Eject_ExW"
...
...
CM_Request_Device_Eject(spdd\DevInst, #Null, #Null$, #Null, #Null, #Null)
I am getting a message that the 'Generic USB Flash Disk USB Device' (friendly name) is not removable. And it cannot be ejected.

I confess honestly that I studied your old code here.
But it seemed confusing to me and I wanted to do something similar, but simpler. Nevertheless, the principle is the same - this is the definition of the device number in the system (and further comparison). I tried adding this piece to your code and it didn't work:

Code: Select all

hUSB = CreateFile_(@pdidd\DevicePath, #GENERIC_READ | #GENERIC_WRITE, #FILE_SHARE_READ | #FILE_SHARE_WRITE, #Null, #OPEN_EXISTING, #FILE_ATTRIBUTE_NORMAL, #Null)
        If hUSB <> #INVALID_HANDLE_VALUE
          If DeviceIoControl_(hUSB, #IOCTL_STORAGE_GET_DEVICE_NUMBER, 0, 0, @sdn, SizeOf(STORAGE_DEVICE_NUMBER), @BytesReturned, 0)
            If sdn\DeviceNumber = TargetDriveNumber
              CloseHandle_(hUSB)
              CM_Request_Device_Eject(spdd\DevInst, #Null, #Null$, #Null, #Null, #Null)
              Break
            EndIf
          EndIf
          CloseHandle_(hUSB)
        EndIf
Of course, the TargetDriveNumber is predefined. What could be the reason? If I do it manually from the taskbar tray, then everything is fine. With the same flash drive.

Thanks again for your help. This is very valuable.
breeze4me
Enthusiast
Enthusiast
Posts: 633
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: About using 'setupapi.dll'

Post by breeze4me »

There are some problems.
1. Invalid parameter values.
2. Not using the parent's device instance handle.

Try again with the code below.

Code: Select all

hUSB = CreateFile_(@pdidd\DevicePath, 0, #FILE_SHARE_READ | #FILE_SHARE_WRITE, #Null, #OPEN_EXISTING, 0, #Null)  ; <-- invalid parameters
If hUSB <> #INVALID_HANDLE_VALUE
  If DeviceIoControl_(hUSB, #IOCTL_STORAGE_GET_DEVICE_NUMBER, 0, 0, @sdn, SizeOf(STORAGE_DEVICE_NUMBER), @BytesReturned, 0)
    If sdn\DeviceNumber = DeviceNumber
      CloseHandle_(hUSB)
      CM_Request_Device_Eject(DevInstParent, #Null, #Null$, #Null, #Null, #Null)     ; <--- parent's devinst
      Break
    EndIf
  EndIf
  CloseHandle_(hUSB)
EndIf

Full code.

Code: Select all

#CR_SUCCESS = 0
#CM_LOCATE_DEVNODE_NORMAL = 0

#MAX_CLASS_NAME_LEN = 32
#MAX_DEV_NAME_LEN   = 512

#SPDRP_HARDWAREID   = 1
#SPDRP_SERVICE      = 4

#SPDRP_FRIENDLYNAME = $0C
#SPDRP_ENUMERATOR_NAME = $16
#SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = $0E

Structure SP_DEVICE_INTERFACE_DETAIL_DATA
  cbSize.l
  DevicePath.s{255}
EndStructure

Structure SP_DEVINFO_DATA
  cbSize.l 
  ClassGuid.GUID 
  DevInst.l 
  *reserved
EndStructure 

Prototype ptCM_Get_Device_IDW(DevInst.l, *DeviceIdString, BufferLen.l, Flags.l)
Prototype ptCM_Get_Parent(*DevInst, DevInst.l, Flags)
Prototype ptCM_Locate_DevNodeW(*DevInst, *pDeviceID, ulFlags.l)
Prototype ptCM_Request_Device_Eject_ExW(DevInst.l, *VetoType, VetoName.s, NameLength.l, Flags.l, hMachine)

Global CM_Request_Device_Eject.ptCM_Request_Device_Eject_ExW
Global CM_Locate_DevNode.ptCM_Locate_DevNodeW
Global CM_Get_Parent.ptCM_Get_Parent
Global CM_Get_Device_ID.ptCM_Get_Device_IDW

Define setupapi

setupapi = OpenLibrary(#PB_Any, "setupapi.dll")
If setupapi
  CM_Get_Device_ID = GetFunction(setupapi, "CM_Get_Device_IDW")
  CM_Get_Parent = GetFunction(setupapi, "CM_Get_Parent")
  CM_Locate_DevNode = GetFunction(setupapi, "CM_Locate_DevNodeW")
  CM_Request_Device_Eject = GetFunction(setupapi, "CM_Request_Device_Eject_ExW")
Else
  MessageRequester("Error", "Can't open library setupapi.dll")
  End
EndIf

Define Index.i, cbRequired.l
Define DeviceInfoData.SP_DEVICE_INTERFACE_DATA

Define pdidd.SP_DEVICE_INTERFACE_DETAIL_DATA
Define spdd.SP_DEVINFO_DATA

Define hDevInfo
Define *pGUID = ?GUID_USB_DEVICE

Define Buffer.s{512}

DeviceInfoData\cbSize = SizeOf(SP_DEVICE_INTERFACE_DATA)


hDevInfo = SetupDiGetClassDevs_(*pGUID, #Null, #Null, #DIGCF_PRESENT | #DIGCF_DEVICEINTERFACE)
If hDevInfo = #INVALID_HANDLE_VALUE
  MessageRequester("Error", "Inavlid handle value")
  End
EndIf

While SetupDiEnumDeviceInterfaces_(hDevInfo, 0, *pGUID, Index, @DeviceInfoData)
  
  SetupDiGetDeviceInterfaceDetail_(hDevInfo, @DeviceInfoData, 0, 0, @cbRequired, 0)
  If cbRequired > 0 And cbRequired < 255
    
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
      pdidd\cbSize = 8      ; x86(Unicode=6, ASCII=5), x64=8
    CompilerElse
      pdidd\cbSize = 4 + SizeOf(Character)   ; x86(Unicode=6, ASCII=5), x64=8
    CompilerEndIf
    
    spdd\cbSize = SizeOf(SP_DEVINFO_DATA)
    
    If SetupDiGetDeviceInterfaceDetail_(hDevInfo, @DeviceInfoData, @pdidd, cbRequired, 0, @spdd)
      
      If FindString(pdidd\DevicePath, "USBSTOR", 1, #PB_String_NoCase)
        
        CM_Get_Parent(@DevInstParent, spdd\DevInst, 0)
        Debug "Parent DevInst: " + DevInstParent
        
        CM_Get_Device_ID(DevInstParent, @Buffer, 512, 0)
        Debug "Parent DeviceId: " + Buffer
        
        Debug ""
        
        Debug "DevicePath: " + pdidd\DevicePath
        Debug "DevInst: " + spdd\DevInst
        
        CM_Get_Device_ID(spdd\DevInst, @Buffer, 512, 0)
        Debug "DeviceId: " + Buffer
        
        CM_Locate_DevNode(@id, @Buffer, #CM_LOCATE_DEVNODE_NORMAL)
        Debug "DevInst: " + id
        
        SetupDiGetDeviceRegistryProperty_(hDevInfo, @spdd, #SPDRP_FRIENDLYNAME, 0, @Buffer, 512, 0)
        Debug "Friendly name: " + Buffer
        
        SetupDiGetDeviceRegistryProperty_(hDevInfo, @spdd, #SPDRP_ENUMERATOR_NAME, 0, @Buffer, 512, 0)
        Debug "SPDRP_ENUMERATOR_NAME: " + Buffer
        
        SetupDiGetDeviceRegistryProperty_(hDevInfo, @spdd, #SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, 0, @Buffer, 512, 0)
        Debug "SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: " + Buffer
        
        SetupDiGetDeviceRegistryProperty_(hDevInfo, @spdd, #SPDRP_HARDWAREID, 0, @Buffer, 512, 0)
        Debug "SPDRP_HARDWAREID: "
        Debug Buffer
        
        StrLength = StringByteLength(Buffer)
        *p = @Buffer + StrLength + 2
        While PeekC(*p)
          s.s = PeekS(*p)
          Debug s
          StrLength = StringByteLength(s)
          *p + StrLength + 2
        Wend
        
        Debug "----------------------------------------"
        
        Define sdn.STORAGE_DEVICE_NUMBER
        
        hUSB = CreateFile_(@pdidd\DevicePath, 0, #FILE_SHARE_READ | #FILE_SHARE_WRITE, #Null, #OPEN_EXISTING, 0, #Null)
        If hUSB <> #INVALID_HANDLE_VALUE
          If DeviceIoControl_(hUSB, #IOCTL_STORAGE_GET_DEVICE_NUMBER, 0, 0, @sdn, SizeOf(STORAGE_DEVICE_NUMBER), @BytesReturned, 0)
            If sdn\DeviceNumber = DeviceNumber
              CloseHandle_(hUSB)
              CM_Request_Device_Eject(DevInstParent, #Null, #Null$, #Null, #Null, #Null)
              Break
            EndIf
          EndIf
          CloseHandle_(hUSB)
        EndIf
      EndIf
    EndIf
  EndIf
  
  Index + 1
Wend

SetupDiDestroyDeviceInfoList_(hDevInfo)
CloseLibrary(setupapi)


DataSection
  GUID_USB_DEVICE:
  Data.l $53f56307
  Data.w $b6bf, $11d0
  Data.b $94, $f2, $00, $a0, $c9, $1e, $fb, $8b
EndDataSection
ZX80
Enthusiast
Enthusiast
Posts: 365
Joined: Mon Dec 12, 2016 1:37 pm

Re: About using 'setupapi.dll'

Post by ZX80 »

breeze4me, thank you very much :!:

Yeah! It does! It works! Well done!

I have some ideas to expand the functionality of 'my' app. This is still an unsystematized set of actions. And I'm still not sure how it will look in the end. Only separate thoughts from the whole program. But without their implementation, I can't move on.
And... May I ask you one more question? Can I request another CM-function from you? :oops:

Okay. Now I'd like to have a reverse function in my arsenal as well. That is, returning the disk to the system after executing the 'eject' function. Of course, the drive is still physically connected to one of the USB socket / port. By the way, how can you find out? Has the user already pulled the cord or not yet? In the case of usb-hdd.

I also read one old topic about it.
Here you can find a complete list of all CM-functions and others. I think so. Thanks to edel.
Can you tell me, which one will help me return the previously removed device back to the system?
And... If it does not complicate you, then please write an example of its use. :oops:
I first encountered with hardware ('iron') processing.

Thank you in advance.
And sorry for the late reply.

P.S. I think this is a hot topic and many people are interested in it as I am. They follow the news here.
ZX80
Enthusiast
Enthusiast
Posts: 365
Joined: Mon Dec 12, 2016 1:37 pm

Re: About using 'setupapi.dll'

Post by ZX80 »

My bad :!:

I re-read that topic again and realized that I most likely need the 'CM_Disable_DevNode' and 'CM_Enable_DevNode' functions.
Okay. Then how do I eject the drive completely (after executing 'CM_Disable_DevNode')? I need to call 'CM_Enable_DevNode' and then 'CM_Request_Device_Eject_ExW', right?
breeze4me
Enthusiast
Enthusiast
Posts: 633
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: About using 'setupapi.dll'

Post by breeze4me »

Here is a long example. :mrgreen:

Since enabling and disabling the devices requires administrator privilege, compile and run the code as administrator privilege.


There are three test modes in the example, so try them in order.
#Test_Mode___Disable_USB
#Test_Mode___Enable_and_Eject_USB
#Test_Mode___Reconnect_USB
If the code is compiled for x86, the reconnection(#Test_Mode___Reconnect_USB) does not work on Windows x64.
Therefore, compile for x64 to run on Windows x64.

Tested on Windows 10 x64 with PB 6.00 b5 x86/x64.

Code: Select all

#CR_SUCCESS = 0
#CR_NO_SUCH_DEVNODE = $0d
#CR_NO_SUCH_DEVINST = #CR_NO_SUCH_DEVNODE

#DN_HAS_PROBLEM = $00000400
#DN_DRIVER_LOADED = $00000002   ;Has Register_Device_Driver
#DN_STARTED =  $00000008        ;Is currently configured

#CM_PROB_DISABLED       = $00000016      ;devinst is disabled
#CM_PROB_HELD_FOR_EJECT = $0000002F      ;The device is offline awaiting removal

#CM_LOCATE_DEVNODE_NORMAL = 0

#MAX_CLASS_NAME_LEN = 32
#MAX_DEV_NAME_LEN   = 512

#SPDRP_HARDWAREID   = 1
#SPDRP_SERVICE      = 4

#SPDRP_FRIENDLYNAME = $0C
#SPDRP_ENUMERATOR_NAME = $16
#SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = $0E

#APPLICATION_ERROR_MASK = $20000000
#ERROR_SEVERITY_ERROR = $C0000000

#ERROR_IN_WOW64  = #APPLICATION_ERROR_MASK | #ERROR_SEVERITY_ERROR | $235   ;$E0000235
#ERROR_NO_SUCH_DEVINST = #APPLICATION_ERROR_MASK | #ERROR_SEVERITY_ERROR | $20B  ;$E000020B

#CM_REENUMERATE_NORMAL             = 0
#CM_REENUMERATE_SYNCHRONOUS        = 1
#CM_REENUMERATE_RETRY_INSTALLATION = 2
#CM_REENUMERATE_ASYNCHRONOUS       = 4

#CM_SETUP_DEVNODE_READY = 0     ;Reenable problem devinst
#CM_SETUP_DEVNODE_RESET = 4     ;Reset problem devinst without starting

#CM_DISABLE_POLITE    = 0       ;Ask the driver
#CM_DISABLE_ABSOLUTE  = 1       ;Don't ask the driver
#CM_DISABLE_HARDWARE  = 2       ;Don't ask the driver, and won't be restarteable
#CM_DISABLE_UI_NOT_OK = 4       ;Don't popup any veto API
#CM_DISABLE_PERSIST   = 8       ;Persists through restart by setting CONFIGFLAG_DISABLED in the registry

#DICS_ENABLE     = 1
#DICS_DISABLE    = 2
#DICS_PROPCHANGE = 3
#DICS_START      = 4
#DICS_STOP       = 5

#DICS_FLAG_GLOBAL         = 1  ;// make change in all hardware profiles
#DICS_FLAG_CONFIGSPECIFIC = 2  ;// make change in specified profile only
#DICS_FLAG_CONFIGGENERAL  = 4  ;// 1 or more hardware profile-specific changes to follow.

#DIF_PROPERTYCHANGE = $12

Structure SP_CLASSINSTALL_HEADER
  cbSize.l
  InstallFunction.l
EndStructure

Structure SP_PROPCHANGE_PARAMS
  ClassInstallHeader.SP_CLASSINSTALL_HEADER
  StateChange.l
  Scope.l
  HwProfile.l
EndStructure

Structure SP_DEVICE_INTERFACE_DETAIL_DATA
  cbSize.l
  DevicePath.s{512}
EndStructure

Structure SP_DEVINFO_DATA
  cbSize.l 
  ClassGuid.GUID 
  DevInst.l 
  *reserved
EndStructure 

Prototype ptCM_Get_Device_IDW(DevInst.l, *DeviceIdString, BufferLen.l, Flags.l)
Prototype ptCM_Get_Parent(*DevInst, DevInst.l, Flags)
Prototype ptCM_Locate_DevNodeW(*DevInst, *pDeviceID, ulFlags.l)
Prototype ptCM_Request_Device_Eject_ExW(DevInst.l, *VetoType, VetoName.s, NameLength.l, Flags.l, hMachine)
Prototype ptCM_Get_DevNode_Status(*pulStatus, *pulProblemNumber, dnDevInst.l, ulFlags.l)
Prototype ptSetupDiSetClassInstallParamsW(DeviceInfoSet, DeviceInfoData, ClassInstallParams, ClassInstallParamsSize.l)
Prototype ptSetupDiCallClassInstaller(InstallFunction, DeviceInfoSet, DeviceInfoData)
Prototype ptCM_Disable_DevNode(DevInst, ulFlags.l)
Prototype ptCM_Enable_DevNode(DevInst, ulFlags.l)
Prototype ptCM_Reenumerate_DevNode(DevInst, ulFlags.l)

Global SetupDiCallClassInstaller.ptSetupDiCallClassInstaller
Global SetupDiSetClassInstallParams.ptSetupDiSetClassInstallParamsW
Global CM_Reenumerate_DevNode.ptCM_Reenumerate_DevNode
Global CM_Disable_DevNode.ptCM_Disable_DevNode
Global CM_Enable_DevNode.ptCM_Enable_DevNode
Global CM_Get_DevNode_Status.ptCM_Get_DevNode_Status
Global CM_Request_Device_Eject.ptCM_Request_Device_Eject_ExW
Global CM_Locate_DevNode.ptCM_Locate_DevNodeW
Global CM_Get_Parent.ptCM_Get_Parent
Global CM_Get_Device_ID.ptCM_Get_Device_IDW

Define setupapi

setupapi = OpenLibrary(#PB_Any, "setupapi.dll")
If setupapi
  CM_Get_Device_ID = GetFunction(setupapi, "CM_Get_Device_IDW")
  CM_Get_Parent = GetFunction(setupapi, "CM_Get_Parent")
  CM_Locate_DevNode = GetFunction(setupapi, "CM_Locate_DevNodeW")
  CM_Request_Device_Eject = GetFunction(setupapi, "CM_Request_Device_Eject_ExW")
  CM_Get_DevNode_Status = GetFunction(setupapi, "CM_Get_DevNode_Status")
  SetupDiSetClassInstallParams = GetFunction(setupapi, "SetupDiSetClassInstallParamsW")
  SetupDiCallClassInstaller = GetFunction(setupapi, "SetupDiCallClassInstaller")
  CM_Enable_DevNode = GetFunction(setupapi, "CM_Enable_DevNode")
  CM_Disable_DevNode = GetFunction(setupapi, "CM_Disable_DevNode")
  CM_Reenumerate_DevNode = GetFunction(setupapi, "CM_Reenumerate_DevNode")
Else
  MessageRequester("Error", "Can't open library setupapi.dll")
  End
EndIf


Define Index, ParentIndex, cbRequired
Define spdid.SP_DEVICE_INTERFACE_DATA
Define spdidParent.SP_DEVICE_INTERFACE_DATA

Define spdidd.SP_DEVICE_INTERFACE_DETAIL_DATA
Define spdiddParent.SP_DEVICE_INTERFACE_DETAIL_DATA

Define spdd.SP_DEVINFO_DATA
Define spddParent.SP_DEVINFO_DATA

Define sppp.SP_PROPCHANGE_PARAMS

Define hDevInfo, hParentDevInfo
Define *pGUID = ?GUID_DEVINTERFACE_DISK

Define Buffer.s{512}
Define ParentDeviceInstID.s{512}
Define sdn.STORAGE_DEVICE_NUMBER

Define Status, StatusParent



;- Flags for test
Enumeration
  #Test_Mode___Disable_USB
  #Test_Mode___Enable_and_Eject_USB
  #Test_Mode___Reconnect_USB
EndEnumeration


;- Set a flag !
#Test_Mode = #Test_Mode___Disable_USB




CompilerSelect #Test_Mode
  CompilerCase #Test_Mode___Disable_USB
    
    hDevInfo = SetupDiGetClassDevs_(*pGUID, #Null, #Null, #DIGCF_PRESENT | #DIGCF_DEVICEINTERFACE)
    
  CompilerCase #Test_Mode___Enable_and_Eject_USB
    
    hDevInfo = SetupDiGetClassDevs_(*pGUID, #Null, #Null, #DIGCF_DEVICEINTERFACE)
    
  CompilerCase #Test_Mode___Reconnect_USB
    
    hDevInfo = SetupDiGetClassDevs_(*pGUID, #Null, #Null, #DIGCF_DEVICEINTERFACE)
    
  CompilerDefault
    
    End
    
CompilerEndSelect


If hDevInfo = #INVALID_HANDLE_VALUE
  MessageRequester("Error", "Inavlid handle value")
  End
EndIf

spdid\cbSize = SizeOf(SP_DEVICE_INTERFACE_DATA)

While SetupDiEnumDeviceInterfaces_(hDevInfo, 0, *pGUID, Index, @spdid)
  
  SetupDiGetDeviceInterfaceDetail_(hDevInfo, @spdid, 0, 0, @cbRequired, 0)
  If cbRequired > 0 And cbRequired < 511
    
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
      spdidd\cbSize = 8      ; x86(Unicode=6, ASCII=5), x64=8
    CompilerElse
      spdidd\cbSize = 4 + SizeOf(Character)   ; x86(Unicode=6, ASCII=5), x64=8
    CompilerEndIf
    
    spdd\cbSize = SizeOf(SP_DEVINFO_DATA)
    
    If SetupDiGetDeviceInterfaceDetail_(hDevInfo, @spdid, @spdidd, cbRequired, 0, @spdd)
      
      If FindString(spdidd\DevicePath, "USBSTOR", 1, #PB_String_NoCase)
        
        If CM_Get_Parent(@DevInstParent, spdd\DevInst, 0) = #CR_SUCCESS
          Debug "Parent DevInst: " + DevInstParent
        EndIf
        
        If CM_Get_Device_ID(DevInstParent, @ParentDeviceInstID, 511, 0) = #CR_SUCCESS
          Debug "Parent Device instance ID: " + ParentDeviceInstID
        EndIf
        
        If CM_Get_DevNode_Status(@StatusParent, @ProblemNumber, DevInstParent, 0) = #CR_SUCCESS
          Debug "     DN_STARTED:  " + Bool(StatusParent & #DN_STARTED)
          Debug "     DN_DRIVER_LOADED:  " + Bool(StatusParent & #DN_DRIVER_LOADED)
          
          
          Debug "*** Child ***"
          Debug "DevicePath: " + spdidd\DevicePath
          Debug "DevInst: " + spdd\DevInst
          
          If CM_Get_DevNode_Status(@Status, @ProblemNumber, spdd\DevInst, 0) = #CR_SUCCESS
            Debug "     DN_STARTED:  " + Bool(Status & #DN_STARTED)
            Debug "     DN_DRIVER_LOADED:  " + Bool(Status & #DN_DRIVER_LOADED)
          EndIf
          
          CM_Get_Device_ID(spdd\DevInst, @Buffer, 512, 0)
          Debug "Device instance ID: " + Buffer
          
          CM_Locate_DevNode(@id, @Buffer, #CM_LOCATE_DEVNODE_NORMAL)
          Debug "DevInst: " + id
          
          SetupDiGetDeviceRegistryProperty_(hDevInfo, @spdd, #SPDRP_FRIENDLYNAME, 0, @Buffer, 1024, 0)
          Debug "Friendly name: " + Buffer
          
          SetupDiGetDeviceRegistryProperty_(hDevInfo, @spdd, #SPDRP_ENUMERATOR_NAME, 0, @Buffer, 1024, 0)
          Debug "SPDRP_ENUMERATOR_NAME: " + Buffer
          
          SetupDiGetDeviceRegistryProperty_(hDevInfo, @spdd, #SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, 0, @Buffer, 1024, 0)
          Debug "SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: " + Buffer
          
          SetupDiGetDeviceRegistryProperty_(hDevInfo, @spdd, #SPDRP_HARDWAREID, 0, @Buffer, 1024, 0)
          Debug "SPDRP_HARDWAREID: "
          Debug Buffer
          
          StrLength = StringByteLength(Buffer)
          *p = @Buffer + StrLength + 2
          While PeekC(*p)
            s.s = PeekS(*p)
            Debug s
            StrLength = StringByteLength(s)
            *p + StrLength + 2
          Wend
          
          Debug ""
          
          
          
          CompilerSelect #Test_Mode
            CompilerCase #Test_Mode___Disable_USB
              
              Debug "****** Disable USB"
              
              If CM_Disable_DevNode(DevInstParent, #CM_DISABLE_UI_NOT_OK) = #CR_SUCCESS
                Debug "   The device is disabled."
                
                Break
              EndIf
              
              
              
            CompilerCase #Test_Mode___Enable_and_Eject_USB
              
              Debug "****** Enable and Eject USB"
              
              If StatusParent & #DN_HAS_PROBLEM
                If ProblemNumber = #CM_PROB_DISABLED
                  Debug "   The device is currently disabled."
                  
                  If CM_Enable_DevNode(DevInstParent, 0) = #CR_SUCCESS
                    CM_Reenumerate_DevNode(DevInstParent, #CM_REENUMERATE_SYNCHRONOUS | #CM_REENUMERATE_RETRY_INSTALLATION)
                    Debug "   The device is enabled."
                    
                    Delay(700)         ; Sometimes the device cannot be ejected, so use the Delay function if necessary.
                    
                  EndIf
                EndIf
              EndIf
              
              hUSB = CreateFile_(@spdidd\DevicePath, 0, #FILE_SHARE_READ | #FILE_SHARE_WRITE, #Null, #OPEN_EXISTING, 0, #Null)
              If hUSB <> #INVALID_HANDLE_VALUE
                If DeviceIoControl_(hUSB, #IOCTL_STORAGE_GET_DEVICE_NUMBER, 0, 0, @sdn, SizeOf(STORAGE_DEVICE_NUMBER), @BytesReturned, 0)
                  If sdn\DeviceNumber        ;= DeviceNumber
                    CloseHandle_(hUSB)
                    CM_Request_Device_Eject(DevInstParent, #Null, #Null$, #Null, #Null, #Null)
                    
                    Debug "   Eject done."
                    
                    Break
                  EndIf
                EndIf
                CloseHandle_(hUSB)
              EndIf
              
              
              
            CompilerCase #Test_Mode___Reconnect_USB
              
              Debug "****** Reconnect USB"
              
              If StatusParent & #DN_HAS_PROBLEM And ProblemNumber = #CM_PROB_HELD_FOR_EJECT
                If CM_Get_DevNode_Status(@StatusParent, @ProblemNumber, spdd\DevInst, 0) = #CR_NO_SUCH_DEVINST
                  
                  Debug "   The device is not physically removed."
                  
                  hParentDevInfo = SetupDiGetClassDevs_(?GUID_DEVINTERFACE_USB_DEVICE, @ParentDeviceInstID, #Null, #DIGCF_DEVICEINTERFACE)
                  If hParentDevInfo <> #INVALID_HANDLE_VALUE
                    
                    spdidParent\cbSize = SizeOf(SP_DEVICE_INTERFACE_DATA)
                    
                    While SetupDiEnumDeviceInterfaces_(hParentDevInfo, 0, ?GUID_DEVINTERFACE_USB_DEVICE, ParentIndex, @spdidParent)
                      SetupDiGetDeviceInterfaceDetail_(hParentDevInfo, @spdidParent, 0, 0, @cbRequired, 0)
                      
                      CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
                        spdiddParent\cbSize = 8      ; x86(Unicode=6, ASCII=5), x64=8
                      CompilerElse
                        spdiddParent\cbSize = 4 + SizeOf(Character)   ; x86(Unicode=6, ASCII=5), x64=8
                      CompilerEndIf
                      
                      spddParent\cbSize = SizeOf(SP_DEVINFO_DATA)
                      
                      If cbRequired > 0 And cbRequired < 511
                        If SetupDiGetDeviceInterfaceDetail_(hParentDevInfo, @spdidParent, @spdiddParent, cbRequired, 0, @spddParent)
                          Debug "Parent DevicePath: " + spdiddParent\DevicePath
                          Debug "Parent DevInst: " + spddParent\DevInst
                          
                          If spddParent\DevInst = DevInstParent
                            Break
                          EndIf
                        EndIf
                      EndIf
                      
                      ParentIndex + 1
                    Wend
                    
                    sppp\ClassInstallHeader\cbSize = SizeOf(SP_CLASSINSTALL_HEADER)
                    sppp\ClassInstallHeader\InstallFunction = #DIF_PROPERTYCHANGE
                    sppp\StateChange = #DICS_PROPCHANGE
                    sppp\Scope = #DICS_FLAG_CONFIGSPECIFIC
                    ;sppp\Scope = #DICS_FLAG_GLOBAL
                    sppp\HwProfile = 0
                    
                    If SetupDiSetClassInstallParams(hParentDevInfo, @spddParent, @sppp, SizeOf(SP_PROPCHANGE_PARAMS))
                      
                      If SetupDiCallClassInstaller(#DIF_PROPERTYCHANGE, hParentDevInfo, @spddParent)
                        
                        If CM_Disable_DevNode(DevInstParent, #CM_DISABLE_UI_NOT_OK) = #CR_SUCCESS
                          If CM_Enable_DevNode(DevInstParent, 0) = #CR_SUCCESS
                            CM_Reenumerate_DevNode(DevInstParent, #CM_REENUMERATE_SYNCHRONOUS | #CM_REENUMERATE_RETRY_INSTALLATION)
                            Debug "   The device reconnected."
                          EndIf
                        EndIf
                        
                      Else
                        If GetLastError_() = #ERROR_IN_WOW64
                          Debug "********** need to be compiled for x64.   x86 app using SetupDiCallClassInstaller API cannot run on Windows x64."
                        EndIf
                      EndIf
                    EndIf
                    
                    SetupDiDestroyDeviceInfoList_(hParentDevInfo)
                  EndIf
                  
                  Break
                  
                EndIf
              EndIf
              
              
              
          CompilerEndSelect
          
          
        EndIf
        
        
        Debug "----------------------------------------------------------------------------------"
      EndIf
    EndIf
  EndIf
  
  Index + 1
Wend

SetupDiDestroyDeviceInfoList_(hDevInfo)
CloseLibrary(setupapi)


DataSection
  GUID_DEVINTERFACE_USB_DEVICE:
  Data.l $A5DCBF10
  Data.w $6530, $11D2
  Data.b $90, $1F, $00, $C0, $4F, $B9, $51, $ED
  
  GUID_DEVINTERFACE_DISK:
  Data.l $53f56307
  Data.w $b6bf, $11d0
  Data.b $94, $f2, $00, $a0, $c9, $1e, $fb, $8b
EndDataSection
ZX80
Enthusiast
Enthusiast
Posts: 365
Joined: Mon Dec 12, 2016 1:37 pm

Re: About using 'setupapi.dll'

Post by ZX80 »

breeze4me, thank you very much for your hard work and detailed answer :!:

I'm so sorry, but I won't be able to test it today. I promise you I will definitely write a report here as soon as I test it. So that we can speak objectively. I'm not ready yet.
I will tell you in advance that your code looks promising.

Once again, thank you very much for your feedback / help.
ZX80
Enthusiast
Enthusiast
Posts: 365
Joined: Mon Dec 12, 2016 1:37 pm

Re: About using 'setupapi.dll'

Post by ZX80 »

Good time, breeze4me.

After short test of your code, I see this in the debug window:
/////////////// First step/run /////////////////

====================================================
#Test_Mode___Disable_USB
====================================================

Parent DevInst: 2340
Parent Device instance ID: USB\VID_XXXX&PID_YYYY\ZZZZZZZZ
DN_STARTED: 1
DN_DRIVER_LOADED: 1
*** Child ***
DevicePath: \\?\usbstor#disk&ven_jetflash&prod_transcend_8gb&rev_8.07#zzzzzzzz&0#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
DevInst: 2204
DN_STARTED: 1
DN_DRIVER_LOADED: 1
Device instance ID: USBSTOR\DISK&VEN_JETFLASH&PROD_TRANSCEND_8GB&REV_8.07\ZZZZZZZZ&0
DevInst: 2204
Friendly name: JetFlash Transcend 8GB USB Device
SPDRP_ENUMERATOR_NAME: USBSTOR
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\00000095
SPDRP_HARDWAREID:
USBSTOR\DiskJetFlashTranscend_8GB___8.07
USBSTOR\DiskJetFlashTranscend_8GB___
USBSTOR\DiskJetFlash
USBSTOR\JetFlashTranscend_8GB___8
JetFlashTranscend_8GB___8
USBSTOR\GenDisk
GenDisk

****** Disable USB
The device is disabled.


////////////// Second step/run /////////////////

====================================================
#Test_Mode___Enable_and_Eject_USB
====================================================

Parent DevInst: 4288
Parent Device instance ID: HTREE\ROOT\0
----------------------------------------------------------------------------------
Parent DevInst: 4288
Parent Device instance ID: HTREE\ROOT\0
----------------------------------------------------------------------------------
Parent DevInst: 4288
Parent Device instance ID: HTREE\ROOT\0
----------------------------------------------------------------------------------
Parent DevInst: 4288
Parent Device instance ID: HTREE\ROOT\0
----------------------------------------------------------------------------------
Parent DevInst: 4288
Parent Device instance ID: HTREE\ROOT\0
----------------------------------------------------------------------------------
Parent DevInst: 4288
Parent Device instance ID: HTREE\ROOT\0
----------------------------------------------------------------------------------
Parent DevInst: 4288
Parent Device instance ID: HTREE\ROOT\0
----------------------------------------------------------------------------------
Parent DevInst: 4288
Parent Device instance ID: HTREE\ROOT\0
----------------------------------------------------------------------------------
Parent DevInst: 4288
Parent Device instance ID: HTREE\ROOT\0
----------------------------------------------------------------------------------
Parent DevInst: 4288
Parent Device instance ID: HTREE\ROOT\0
----------------------------------------------------------------------------------
Parent DevInst: 4288
Parent Device instance ID: HTREE\ROOT\0
----------------------------------------------------------------------------------
Parent DevInst: 4288
Parent Device instance ID: HTREE\ROOT\0
----------------------------------------------------------------------------------
Parent DevInst: 4288
Parent Device instance ID: HTREE\ROOT\0
----------------------------------------------------------------------------------
Maybe I did something wrong? But I followed your instructions and ran the code twice with 'F5' (compile and run). Yes, I am owner of my machine and I am an administrator (all rights are ok). The first launch was with the '#Test_Mode___Disable_USB' flag, and the second with the '#Test_Mode___Enable_and_Eject_USB' flag. Between these launches, the flash drive was in its socket all time. After reviewing the report, I realized that testing the third flag was useless. Although I did this and got the same result as in the second step. I'm on windows 7 x86 and PB 5.73 x86. I continue to research your code. In any case, there is a lot of useful information there. Maybe I'll change the concept if I can't get your code to work. I also thought about saving disk information during the first step. And then put that data into a struct in the second step.

You probably heard about 'USB Safely Remove'. I don't really know how it works, but it works (about reconnection of a disk without physical removal). 'USB Safely Remove' or 'Zentimo' by crystalrich. I tried to repeat the same trick in my program. In fact, this is not my ultimate goal, but only one step towards it.
I wanted to create something like 'DeviceLock' (everything is very serious and works at the driver level) in a very simplified form, of course. And only for removable devices (for protection against unauthorized access). This was my old dream. I do not like the option to disable ports in bios. Doesn't matter if it's front-only or everyone. Here you go. I've spilled all my secrets. :mrgreen:

Although I can't put together all the information I've got here to get the overall picture (I hope this is temporary). I am thinking about creating a module just for disk operations. So I don't have to look at that hard work (no distractions) when writing the main code. Also I don't have to care about unique variable names (it's isolated). I'm sorry, but I cannot fail to mention here the fantastic HID-module by User_Russian (for microcontrollers). It's actually very well done. I'll try to do something like this, but for removable drives.

In any case, you have already helped me a lot. Thank you for that, breeze4me. I wouldn't have done it alone. I thought long about resolve this issue. Thank you, you saved me a lot of time. As you can see, I have peaceful purposes.
breeze4me
Enthusiast
Enthusiast
Posts: 633
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: About using 'setupapi.dll'

Post by breeze4me »

Here is the code for Windows 7 x86.
It's different from the previous code, so be careful when you use a different version of Windows.
Of course, this code should be executed as an administrator privilege, too.

Turn off the "AutoPlay" feature of Windows before running. Otherwise, when enabling the disabled device again and ejecting it, it will not be ejected, saying that the device is in use.
This is because an explorer window related to the AutoPlay is opened.
---------------------------
Problem Ejecting USB Mass Storage Device
---------------------------
This device is currently in use. Close any programs or windows that might be using the device, and then try again.
Alternatively, there is a way to prevent the AutoPlay using the "QueryCancelAutoPlay" window message, so try searching.
A foreground window is essential to process the message.

Code: Select all

RegisterWindowMessage_("QueryCancelAutoPlay")
Since the "USB Root Hub" device is manipulated when reconnecting, it may affect other associated devices.

Edit 1:
There is something missing in the code.
When disabling a device, enumerate the child "storage" devices of the device to see if there is any device you want to disable.

Edit 2:
Corrected the problem pointed out in "Edit 1".

Code: Select all

#CR_SUCCESS = 0
#CR_NO_SUCH_DEVNODE = $0d
#CR_NO_SUCH_DEVINST = #CR_NO_SUCH_DEVNODE

#DN_HAS_PROBLEM = $00000400
#DN_DRIVER_LOADED = $00000002   ;Has Register_Device_Driver
#DN_STARTED =  $00000008        ;Is currently configured

#CM_PROB_DISABLED       = $00000016      ;devinst is disabled
#CM_PROB_HELD_FOR_EJECT = $0000002F      ;The device is offline awaiting removal

#CM_LOCATE_DEVNODE_NORMAL = 0

#MAX_CLASS_NAME_LEN = 32
#MAX_DEV_NAME_LEN   = 512

#SPDRP_HARDWAREID   = 1
#SPDRP_SERVICE      = 4

#SPDRP_FRIENDLYNAME = $0C
#SPDRP_ENUMERATOR_NAME = $16
#SPDRP_PHYSICAL_DEVICE_OBJECT_NAME = $0E

#APPLICATION_ERROR_MASK = $20000000
#ERROR_SEVERITY_ERROR = $C0000000

#ERROR_IN_WOW64  = #APPLICATION_ERROR_MASK | #ERROR_SEVERITY_ERROR | $235   ;$E0000235
#ERROR_NO_SUCH_DEVINST = #APPLICATION_ERROR_MASK | #ERROR_SEVERITY_ERROR | $20B  ;$E000020B

#CM_REENUMERATE_NORMAL             = 0
#CM_REENUMERATE_SYNCHRONOUS        = 1
#CM_REENUMERATE_RETRY_INSTALLATION = 2
#CM_REENUMERATE_ASYNCHRONOUS       = 4

#CM_SETUP_DEVNODE_READY = 0     ;Reenable problem devinst
#CM_SETUP_DEVNODE_RESET = 4     ;Reset problem devinst without starting

#CM_DISABLE_POLITE    = 0       ;Ask the driver
#CM_DISABLE_ABSOLUTE  = 1       ;Don't ask the driver
#CM_DISABLE_HARDWARE  = 2       ;Don't ask the driver, and won't be restarteable
#CM_DISABLE_UI_NOT_OK = 4       ;Don't popup any veto API
#CM_DISABLE_PERSIST   = 8       ;Persists through restart by setting CONFIGFLAG_DISABLED in the registry

#DICS_ENABLE     = 1
#DICS_DISABLE    = 2
#DICS_PROPCHANGE = 3
#DICS_START      = 4
#DICS_STOP       = 5

#DICS_FLAG_GLOBAL         = 1  ;// make change in all hardware profiles
#DICS_FLAG_CONFIGSPECIFIC = 2  ;// make change in specified profile only
#DICS_FLAG_CONFIGGENERAL  = 4  ;// 1 or more hardware profile-specific changes to follow.

#DIF_PROPERTYCHANGE = $12

Structure SP_CLASSINSTALL_HEADER
  cbSize.l
  InstallFunction.l
EndStructure

Structure SP_PROPCHANGE_PARAMS
  ClassInstallHeader.SP_CLASSINSTALL_HEADER
  StateChange.l
  Scope.l
  HwProfile.l
EndStructure

Structure SP_DEVICE_INTERFACE_DETAIL_DATA
  cbSize.l
  DevicePath.s{512}
EndStructure

Structure SP_DEVINFO_DATA
  cbSize.l 
  ClassGuid.GUID 
  DevInst.l 
  *reserved
EndStructure 

Prototype ptCM_Get_Device_IDW(DevInst.l, *DeviceIdString, BufferLen.l, Flags.l)
Prototype ptCM_Get_Parent(*DevInst, DevInst.l, Flags)
Prototype ptCM_Locate_DevNodeW(*DevInst, *pDeviceID, ulFlags.l)
Prototype ptCM_Get_Child(*DevInst, DevInst, ulFlags.l)
Prototype ptCM_Request_Device_Eject_ExW(DevInst.l, *VetoType, VetoName.s, NameLength.l, Flags.l, hMachine)
Prototype ptCM_Get_DevNode_Status(*Status, *ProblemNumber, dnDevInst.l, ulFlags.l)
Prototype ptSetupDiSetClassInstallParamsW(DeviceInfoSet, DeviceInfoData, ClassInstallParams, ClassInstallParamsSize.l)
Prototype ptSetupDiCallClassInstaller(InstallFunction, DeviceInfoSet, DeviceInfoData)
Prototype ptCM_Disable_DevNode(DevInst, ulFlags.l)
Prototype ptCM_Enable_DevNode(DevInst, ulFlags.l)
Prototype ptCM_Reenumerate_DevNode(DevInst, ulFlags.l)
Prototype ptCM_Get_Sibling(*DevInst, DevInst, ulFlags.l)

Global CM_Get_Sibling.ptCM_Get_Sibling
Global CM_Get_Child.ptCM_Get_Child
Global SetupDiCallClassInstaller.ptSetupDiCallClassInstaller
Global SetupDiSetClassInstallParams.ptSetupDiSetClassInstallParamsW
Global CM_Reenumerate_DevNode.ptCM_Reenumerate_DevNode
Global CM_Disable_DevNode.ptCM_Disable_DevNode
Global CM_Enable_DevNode.ptCM_Enable_DevNode
Global CM_Get_DevNode_Status.ptCM_Get_DevNode_Status
Global CM_Request_Device_Eject.ptCM_Request_Device_Eject_ExW
Global CM_Locate_DevNode.ptCM_Locate_DevNodeW
Global CM_Get_Parent.ptCM_Get_Parent
Global CM_Get_Device_ID.ptCM_Get_Device_IDW

Define setupapi

setupapi = OpenLibrary(#PB_Any, "setupapi.dll")
If setupapi
  CM_Get_Device_ID = GetFunction(setupapi, "CM_Get_Device_IDW")
  CM_Get_Parent = GetFunction(setupapi, "CM_Get_Parent")
  CM_Get_Child = GetFunction(setupapi, "CM_Get_Child")
  CM_Get_Sibling = GetFunction(setupapi, "CM_Get_Sibling")
  CM_Locate_DevNode = GetFunction(setupapi, "CM_Locate_DevNodeW")
  CM_Request_Device_Eject = GetFunction(setupapi, "CM_Request_Device_Eject_ExW")
  CM_Get_DevNode_Status = GetFunction(setupapi, "CM_Get_DevNode_Status")
  SetupDiSetClassInstallParams = GetFunction(setupapi, "SetupDiSetClassInstallParamsW")
  SetupDiCallClassInstaller = GetFunction(setupapi, "SetupDiCallClassInstaller")
  CM_Enable_DevNode = GetFunction(setupapi, "CM_Enable_DevNode")
  CM_Disable_DevNode = GetFunction(setupapi, "CM_Disable_DevNode")
  CM_Reenumerate_DevNode = GetFunction(setupapi, "CM_Reenumerate_DevNode")
Else
  MessageRequester("Error", "Can't open library setupapi.dll")
  End
EndIf


Define Index, ChildIndex, cbRequired
Define spdid.SP_DEVICE_INTERFACE_DATA
Define spdidChild.SP_DEVICE_INTERFACE_DATA

Define spdidd.SP_DEVICE_INTERFACE_DETAIL_DATA
Define spdiddChild.SP_DEVICE_INTERFACE_DETAIL_DATA

Define spdd.SP_DEVINFO_DATA
Define spddChild.SP_DEVINFO_DATA

Define sppp.SP_PROPCHANGE_PARAMS

Define hDevInfo, hChildDevInfo
Define *pGUID = ?GUID_DEVINTERFACE_USB_DEVICE

Define Buffer.s{512}
Define ParentDeviceInstID.s{512}
Define sdn.STORAGE_DEVICE_NUMBER

Define Status, StatusParent



;- Flags for test
Enumeration
  #Test_Mode___Disable_USB
  #Test_Mode___Enable_and_Eject_USB
  #Test_Mode___Reconnect_USB
EndEnumeration


;- Set a flag !
#Test_Mode = #Test_Mode___Disable_USB




CompilerSelect #Test_Mode
  CompilerCase #Test_Mode___Disable_USB
    
    hDevInfo = SetupDiGetClassDevs_(*pGUID, #Null, #Null, #DIGCF_PRESENT | #DIGCF_DEVICEINTERFACE)
    
  CompilerCase #Test_Mode___Enable_and_Eject_USB
    
    hDevInfo = SetupDiGetClassDevs_(*pGUID, #Null, #Null, #DIGCF_DEVICEINTERFACE)
    
  CompilerCase #Test_Mode___Reconnect_USB
    
    hDevInfo = SetupDiGetClassDevs_(*pGUID, #Null, #Null, #DIGCF_DEVICEINTERFACE)
    
  CompilerDefault
    
    End
    
CompilerEndSelect


If hDevInfo = #INVALID_HANDLE_VALUE
  MessageRequester("Error", "Inavlid handle value")
  End
EndIf

spdid\cbSize = SizeOf(SP_DEVICE_INTERFACE_DATA)

While SetupDiEnumDeviceInterfaces_(hDevInfo, 0, *pGUID, Index, @spdid)
  
  SetupDiGetDeviceInterfaceDetail_(hDevInfo, @spdid, 0, 0, @cbRequired, 0)
  If cbRequired > 0 And cbRequired < 511
    
    CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
      spdidd\cbSize = 8      ; x86(Unicode=6, ASCII=5), x64=8
    CompilerElse
      spdidd\cbSize = 4 + SizeOf(Character)   ; x86(Unicode=6, ASCII=5), x64=8
    CompilerEndIf
    
    spdd\cbSize = SizeOf(SP_DEVINFO_DATA)
    
    If SetupDiGetDeviceInterfaceDetail_(hDevInfo, @spdid, @spdidd, cbRequired, 0, @spdd)
      
      Debug "DevicePath: " + spdidd\DevicePath
      Debug "DevInst: " + spdd\DevInst
      
      If FindString(spdidd\DevicePath, "USB", 1, #PB_String_NoCase)
        
        If CM_Get_Parent(@DevInstParent, spdd\DevInst, 0) = #CR_SUCCESS
          Debug "Parent DevInst: " + DevInstParent
        EndIf
        
        If CM_Get_Device_ID(DevInstParent, @ParentDeviceInstID, 511, 0) = #CR_SUCCESS
          Debug "Parent Device instance ID: " + ParentDeviceInstID
        EndIf
        
        If CM_Get_Child(@DevInstChild, spdd\DevInst, 0) = #CR_SUCCESS
          Debug "Child DevInst: " + DevInstChild
        EndIf
        
        If CM_Get_Device_ID(DevInstChild, @Buffer, 511, 0) = #CR_SUCCESS
          Debug "Child Device instance ID: " + Buffer
        EndIf
        
        If CM_Get_DevNode_Status(@StatusParent, @ProblemNumber, DevInstParent, 0) = #CR_SUCCESS
          Debug "     DN_STARTED:  " + Bool(StatusParent & #DN_STARTED)
          Debug "     DN_DRIVER_LOADED:  " + Bool(StatusParent & #DN_DRIVER_LOADED)
          
          
          Debug "*** Child ***"
          Debug "DevicePath: " + spdidd\DevicePath
          Debug "DevInst: " + spdd\DevInst
          
          If CM_Get_DevNode_Status(@Status, @ProblemNumber, spdd\DevInst, 0) = #CR_SUCCESS
            Debug "     DN_STARTED:  " + Bool(Status & #DN_STARTED)
            Debug "     DN_DRIVER_LOADED:  " + Bool(Status & #DN_DRIVER_LOADED)
          EndIf
          
          CM_Get_Device_ID(spdd\DevInst, @Buffer, 511, 0)
          Debug "Device instance ID: " + Buffer
          
          CM_Locate_DevNode(@id, @Buffer, #CM_LOCATE_DEVNODE_NORMAL)
          Debug "DevInst: " + id
          
          SetupDiGetDeviceRegistryProperty_(hDevInfo, @spdd, #SPDRP_FRIENDLYNAME, 0, @Buffer, 1024, 0)
          Debug "Friendly name: " + Buffer
          
          SetupDiGetDeviceRegistryProperty_(hDevInfo, @spdd, #SPDRP_ENUMERATOR_NAME, 0, @Buffer, 1024, 0)
          Debug "SPDRP_ENUMERATOR_NAME: " + Buffer
          
          SetupDiGetDeviceRegistryProperty_(hDevInfo, @spdd, #SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, 0, @Buffer, 1024, 0)
          Debug "SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: " + Buffer
          
          SetupDiGetDeviceRegistryProperty_(hDevInfo, @spdd, #SPDRP_HARDWAREID, 0, @Buffer, 1024, 0)
          Debug "SPDRP_HARDWAREID: "
          Debug Buffer
          
          StrLength = StringByteLength(Buffer)
          *p = @Buffer + StrLength + 2
          While PeekC(*p)
            s.s = PeekS(*p)
            Debug s
            StrLength = StringByteLength(s)
            *p + StrLength + 2
          Wend
          
          Debug ""
          
          
          
          CompilerSelect #Test_Mode
            CompilerCase #Test_Mode___Disable_USB
              
              Debug "****** Disable USB"
              
              
              Found = 0
              
              If CM_Get_Child(@DevInstChild, spdd\DevInst, 0) = #CR_SUCCESS
                Debug "Child DevInst: " + DevInstChild
                
                If CM_Get_Device_ID(DevInstChild, @Buffer, 511, 0) = #CR_SUCCESS
                  Debug "Child Device instance ID: " + Buffer
                  
                  Found = 1
                  
                  
                  
                  While Not (FindString(Buffer, "USBSTOR", 1, #PB_String_NoCase) And FindString(Buffer, "DISK", 1, #PB_String_NoCase))
                    If CM_Get_Sibling(@DevInstChild, DevInstChild, 0) = #CR_SUCCESS
                      Debug "Sibling DevInst: " + DevInstChild
                      
                      If CM_Get_Device_ID(DevInstChild, @Buffer, 511, 0) = #CR_SUCCESS
                        Debug "Sibling Device instance ID: " + Buffer
                      EndIf
                    Else
                      
                      Debug " ***  No storage devices were found."
                      
                      Found = 0
                      Break
                    EndIf
                  Wend
                  
                EndIf
              EndIf
              
              If Found
                If CM_Disable_DevNode(spdd\DevInst, #CM_DISABLE_UI_NOT_OK) = #CR_SUCCESS
                  Debug "   The device is disabled."
                  
                  Break
                EndIf
              EndIf
              
              
              
              
              
            CompilerCase #Test_Mode___Enable_and_Eject_USB
              
              Debug "****** Enable and Eject USB"
              
              If Status & #DN_HAS_PROBLEM And ProblemNumber = #CM_PROB_DISABLED
                Debug "   The device is currently disabled."
                
                If CM_Enable_DevNode(spdd\DevInst, 0) = #CR_SUCCESS
                  CM_Reenumerate_DevNode(spdd\DevInst, #CM_REENUMERATE_SYNCHRONOUS | #CM_REENUMERATE_RETRY_INSTALLATION)
                  Debug "   The device is enabled."
                  
                  Delay(1500)
                  
                EndIf
              EndIf
              
              If CM_Get_Child(@DevInstChild, spdd\DevInst, 0) = #CR_SUCCESS
                Debug "Child DevInst: " + DevInstChild
                
                If CM_Get_Device_ID(DevInstChild, @Buffer, 511, 0) = #CR_SUCCESS
                  Debug "Child Device instance ID: " + Buffer
                  
                  Found = 1
                  
                  While Not (FindString(Buffer, "USBSTOR", 1, #PB_String_NoCase) And FindString(Buffer, "DISK", 1, #PB_String_NoCase))
                    
                    If CM_Get_Sibling(@DevInstChild, DevInstChild, 0) = #CR_SUCCESS
                      Debug "Sibling DevInst: " + DevInstChild
                      
                      If CM_Get_Device_ID(DevInstChild, @Buffer, 511, 0) = #CR_SUCCESS
                        Debug "Sibling Device instance ID: " + Buffer
                      EndIf
                    Else
                      Found = 0
                      Break
                    EndIf
                  Wend
                  
                  If Found
                    hChildDevInfo = SetupDiGetClassDevs_(?GUID_DEVINTERFACE_DISK, @Buffer, #Null, #DIGCF_PRESENT | #DIGCF_DEVICEINTERFACE)
                    If hChildDevInfo <> #INVALID_HANDLE_VALUE
                      spdidChild\cbSize = SizeOf(SP_DEVICE_INTERFACE_DATA)
                      
                      While SetupDiEnumDeviceInterfaces_(hChildDevInfo, 0, ?GUID_DEVINTERFACE_DISK, ChildIndex, @spdidChild)
                        SetupDiGetDeviceInterfaceDetail_(hChildDevInfo, @spdidChild, 0, 0, @cbRequired, 0)
                        
                        CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
                          spdiddChild\cbSize = 8      ; x86(Unicode=6, ASCII=5), x64=8
                        CompilerElse
                          spdiddChild\cbSize = 4 + SizeOf(Character)   ; x86(Unicode=6, ASCII=5), x64=8
                        CompilerEndIf
                        
                        spddChild\cbSize = SizeOf(SP_DEVINFO_DATA)
                        
                        If cbRequired > 0 And cbRequired < 511
                          If SetupDiGetDeviceInterfaceDetail_(hChildDevInfo, @spdidChild, @spdiddChild, cbRequired, 0, @spddChild)
                            
                            hUSB = CreateFile_(@spdiddChild\DevicePath, 0, #FILE_SHARE_READ | #FILE_SHARE_WRITE, #Null, #OPEN_EXISTING, 0, #Null)
                            If hUSB <> #INVALID_HANDLE_VALUE
                              If DeviceIoControl_(hUSB, #IOCTL_STORAGE_GET_DEVICE_NUMBER, 0, 0, @sdn, SizeOf(STORAGE_DEVICE_NUMBER), @BytesReturned, 0)
                                If sdn\DeviceNumber        ;= DeviceNumber
                                  CloseHandle_(hUSB)
                                  CM_Request_Device_Eject(spdd\DevInst, #Null, #Null$, #Null, #Null, #Null)
                                  
                                  Debug "   Eject done."
                                  
                                  Break
                                EndIf
                              EndIf
                              CloseHandle_(hUSB)
                            EndIf
                            
                          EndIf
                        EndIf
                        
                        ChildIndex + 1
                      Wend
                      
                      SetupDiDestroyDeviceInfoList_(hChildDevInfo)
                    EndIf
                  EndIf
                  
                EndIf
              EndIf
              
              
              
              
              
            CompilerCase #Test_Mode___Reconnect_USB
              
              Debug "****** Reconnect USB"
              
              If Status & #DN_HAS_PROBLEM And ProblemNumber = #CM_PROB_HELD_FOR_EJECT
                Debug "   The device is not physically removed."
                
                If FindString(ParentDeviceInstID, "HUB", 1, #PB_String_NoCase)
                  If CM_Disable_DevNode(DevInstParent, #CM_DISABLE_UI_NOT_OK) = #CR_SUCCESS
                    If CM_Enable_DevNode(DevInstParent, 0) = #CR_SUCCESS
                      
                      CM_Reenumerate_DevNode(DevInstParent, #CM_REENUMERATE_SYNCHRONOUS | #CM_REENUMERATE_RETRY_INSTALLATION)
                      Debug "   The device reconnected."
                      
                      Break
                    EndIf
                  EndIf
                EndIf
              EndIf
              
              
              
          CompilerEndSelect
          
          
        EndIf
        
        
        Debug "----------------------------------------------------------------------------------"
      EndIf
    EndIf
  EndIf
  
  Index + 1
Wend

SetupDiDestroyDeviceInfoList_(hDevInfo)
CloseLibrary(setupapi)


DataSection
  GUID_DEVINTERFACE_USB_DEVICE:
  Data.l $A5DCBF10
  Data.w $6530, $11D2
  Data.b $90, $1F, $00, $C0, $4F, $B9, $51, $ED
  
  GUID_DEVINTERFACE_DISK:
  Data.l $53f56307
  Data.w $b6bf, $11d0
  Data.b $94, $f2, $00, $a0, $c9, $1e, $fb, $8b
EndDataSection
ZX80
Enthusiast
Enthusiast
Posts: 365
Joined: Mon Dec 12, 2016 1:37 pm

Re: About using 'setupapi.dll'

Post by ZX80 »

Good time, breeze4me.

I increased delay up to 10 seconds in your last code and now I got following results:
"#Test_Mode___Disable_USB" .................... success
"#Test_Mode___Enable_and_Eject_USB" ...... success

"#Test_Mode___Reconnect_USB" ................ fail
In the third step, I see my flash-drive and following strings in debug window:
****** Reconnect USB
The device is Not physically removed.
...
----------------------------------------------------------------------------------
DevicePath: \\?\usb#vid_XXXX&pid_YYYY#ZZZZZZZZ#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
DevInst: 2608
Parent DevInst: 3668
Parent Device instance ID: USB\VID_8087&PID_0020\5&35F39389&0&1
DN_STARTED: 1
DN_DRIVER_LOADED: 1
*** Child ***
DevicePath: \\?\usb#vid_XXXX&pid_YYYY#ZZZZZZZZ#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
DevInst: 2608
DN_STARTED: 0
DN_DRIVER_LOADED: 0
Device instance ID: USB\VID_XXXX&PID_YYYY\ZZZZZZZZ
DevInst: 2608
Friendly name: USB\VID_XXXX&PID_YYYY\ZZZZZZZZ
SPDRP_ENUMERATOR_NAME: USB
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME: \Device\USBPDO-5
SPDRP_HARDWAREID:
USB\VID_XXXX&PID_YYYY&REV_0100
USB\VID_XXXX&PID_YYYY

****** Reconnect USB
The device is not physically removed.
----------------------------------------------------------------------------------
...
I think my issue was resolved (given the restrictions that were announced/defined in your previous post). I stick to following rule... "to be simple means reliable".
I revised the concept, like I said. I decided to use only eject-function. If a flash drive is detected that is not on white list, it will be removed immediately. I have it possible to launch such drives, if necessary (quick and temporary access to the unknown flash drives).

All your codes posted here wasn't in vain. It's a good supplement for many other actions (when working with other equipment).
I thank you for everything again and again. For your attention, time and for sharing your skills.


Thank you!....................Thank you!....................Thank you!
Post Reply