Scan for hardware changes

From NSIS Wiki
Jump to navigationJump to search

Description

This is a small file that can be used as a header file for users installing device drivers.

In the event that your hardware driver must first refresh the device list before installation - to prevent a driver pre-install when the hardware is present, but the user manually removed the device from the device manager, for example - the macro below can be used to perform the same as pressing the "Scan for hardware changes" button in the Device Manager.

Links

The Script

!include "LogicLib.nsh"
 
!define CM_LOCATE_DEVNODE_NORMAL 0x00000000
!define CM_REENUMERATE_NORMAL 0x00000000
!define CR_SUCCESS 0x00000000
 
!macro scanForHardwareChanges
  ; Used to store the last return code
  Push $1             ; Stack: $2
  ; Used for temporary storage of DevInst handle
  Push $0             ; Stack: $0 $1
 
  ; Locate the top node (null)
  System::Call "cfgmgr32::CM_Locate_DevNode(*i.r0, n, i${CM_LOCATE_DEVNODE_NORMAL}) i.r1"
 
  ; Check for success
  ${If} $1 <> ${CR_SUCCESS}
      ; If not successful, push '1' to stack to indicate error in Locate
      Push 1          ; Stack: 1 $0 $1
  ${Else}
    ; Otherwise, Re-enumerate the devices.  This can take a few seconds
    System::Call "cfgmgr32::CM_Reenumerate_DevNode(ir0, i${CM_REENUMERATE_NORMAL}) i.r1"
 
    ; Check for success.
    ${If} $1 <> ${CR_SUCCESS}
        ; If not successful, push '2' to stack to indicate error in Re-enumate
        Push 2      ; Stack: 2 $0 $1
    ${Else}
        ; Otherwise, push '0' to stack to indicate no error encountered
        Push 0
    ${EndIf}
  ${EndIf}
                      ; Stack: <result> $0 $1
  Exch                ; Stack: $0 <result> $1
  Pop $0              ; Stack: <result> $1
  Exch                ; Stack: $1 <result>
  Exch $1             ; Stack: <return> <result>
  Exch                ; Stack: <result> <return>
!macroend
!define scanForHardwareChanges `!insertmacro scanForHardwareChanges`

Usage

Simply insert the following in a runtime code section to perform a device list refresh:

${scanForHardwareChanges}

The script pushes two values to the stack that you can optionally Pop to check (or leave the stack dirty if not needed, but beware of scripts that used an empty stack to check for end-of-list/array functionality).

The top level stack item contains the result of the device list refresh.

  • 0 = No errors
  • 1 = An error occurred while trying to get the device instance for the machine
  • 2 = An error occurred while trying to refresh the device list

The second item on the stack contains the return code from the last call executed. This return code can be compared to one of the CR_* return codes defined in cfgmgr32.h . The success return code is defined in the header, ${CR_SUCCESS}, whose value is 0x00000000 or just plain 0 (zero).

${scanForHardwareChanges}
Pop $0 ; macro result code
Pop $1 ; last call return code

Testing

Unless you have a custom device that you are installing/uninstalling, testing this script may seem to be a conundrum; after all, simply uninstalling some random device that your machine depends on to operate is not recommended.

This author has safely tested operation on a laptop with a built-in modem, type "Agere Systems AC'97" - a Softmodem/Winmodem, which hasn't ever seen use due to the ubiquity of wireless and wired internet on any remote locations, and may prove to be a similarly suitable target on other systems.