Is there any way to list all removable and non-removable USB drives in NSIS (NullSoft Install System)? The closest thing I can do at the moment is to use built-in "GetDrives" function and specify HDD, FDD (which lists all USB removable drives, but not USB non-removable), CDROM or NET. I have also found "GetFirstRemovable" plugin, but that too only lists removable drives. If I plug my external 1TB USB drive which is not configured as a "removable" device, it won't show it as FDD, it shows it as HDD so I can't filter by drive type. This also limits any drive that isn't formatted and/or doesn't have a letter assigned.
I need to list all USB drives so I could format it once selected. The way I do it in my PowerShell install script using Get-Disk is like this:
# Only get a list of removable devices (ignore internal drives, cdroms etc.)
$drives = Get-Disk | Where-Object -FilterScript {$_.Bustype -Eq "USB"} | Select-Object -Property DiskNumber, FriendlyName, @{Label = "Size" ; Expression ={ [Math]::Round($_.Size/1GB,0)}} | Sort-Object -Property DiskNumber
But I would like to move from PowerShell (as Get-Disk is only supported on Windows 8+), not all customised systems have PowerShell available and I need it to be cross compatible as much as possible.
I have a feeling I would have to use "Kernel32::" approach, but I have no idea which function from the Kernel32 to use in order to get the list of physical drives.
At this point, I'm nearly ready to abandon the NSIS, which is otherwise a perfect fit for the isntaller I want to make.
I did search around extensively, but all I get is Volume data, but I need Physical disk data which I could filter by bus type.
Any help would be greatly appreaciated.
NSIS has no native support for this so yes, you need to call kernel32 directly:
!include LogicLib.nsh
Section
StrCpy $1 "D:" ; Drive letter
!define DRIVE_REMOVABLE 2
!define DRIVE_FIXED 3
System::Call 'KERNEL32::GetDriveType(t "$1\")i.r0'
${If} $0 = ${DRIVE_REMOVABLE}
DetailPrint DRIVE_REMOVABLE
${ElseIf} $0 = ${DRIVE_FIXED}
DetailPrint DRIVE_FIXED
${Else}
DetailPrint DRIVE_?
${EndIf}
!define OPEN_EXISTING 3
!define FILE_FLAG_NO_BUFFERING 0x20000000
System::Call 'KERNEL32::CreateFile(t "\\.\$1", i0, i7, p0, i${OPEN_EXISTING}, i{FILE_FLAG_NO_BUFFERING}, p0)i.r2'
${If} $2 P= -1
DetailPrint "Unable to open volume $1"
Quit
${EndIf}
!define IOCTL_STORAGE_GET_HOTPLUG_INFO 0x002d0c14
System::Call 'KERNEL32::DeviceIoControl(pr2, i${IOCTL_STORAGE_GET_HOTPLUG_INFO}, p0, i0, @r3, i8, *i, p0)i.r0'
${If} $0 <> 0
System::Call '*$3(i, &i1.r4, &i1.r5, &i1.r6)'
DetailPrint "MediaRemovable=$4 MediaHotplug=$5 DeviceHotplug=$6"
${EndIf}
!define IOCTL_STORAGE_QUERY_PROPERTY 0x002d1400
System::Call 'GDI32::X(@r3)' ; Just "allocate" $3
System::Call '*$3(i0, i0)'
System::Call 'KERNEL32::DeviceIoControl(pr2, i${IOCTL_STORAGE_QUERY_PROPERTY}, pr3, i12, @r4, i100, *i, p0)i.r0'
${If} $0 <> 0
System::Call '*$4(i,i,i,i,i,i,i,&i1.r5)'
DetailPrint "BusType=$5 (BusTypeUsb is 7)"
${EndIf}
FileClose $2
SectionEnd
Enumerating valid drive letters:
Section GetLogicalDrives
System::Call 'KERNEL32::GetLogicalDriveStringsW(i999, @r9)'
loop:
System::Call '*$9(&w4.r8)'
${If} $8 != ""
IntPtrOp $9 $9 + 8
StrCpy $8 $8 2
DetailPrint "Drive $8"
Goto loop
${EndIf}
SectionEnd