You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在NSIS中列出所有可移动与不可移动USB驱动器?

在NSIS中枚举所有USB连接的驱动器(含不可移动外置硬盘与无盘符设备)

要解决你的问题,不能依赖NSIS内置函数或仅识别可移动设备的插件,必须通过Windows内核API直接查询物理磁盘的总线类型,以此精准筛选所有USB连接的存储设备,兼容Windows XP及以上系统,无需PowerShell。

核心思路是:

  • 枚举系统中的物理磁盘(\\.\PhysicalDriveN)
  • 通过DeviceIoControl发送IOCTL_STORAGE_QUERY_PROPERTY请求,获取磁盘的总线类型(BusType)
  • 若BusType为USB,则进一步枚举该磁盘的分区/卷,关联逻辑盘符(如果存在)
  • 同时处理无盘符的USB存储设备

以下是完整的NSIS实现代码:

!include LogicLib.nsh
!include WinCore.nsh

; 定义所需API常量与结构
!define IOCTL_STORAGE_QUERY_PROPERTY 0x2D1400
!define StorageDeviceProperty 0x00
!define PropertyStandardQuery 0x00

enum STORAGE_BUS_TYPE {
    BusTypeUnknown = 0,
    BusTypeScsi,
    BusTypeAtapi,
    BusTypeAta,
    BusType1394,
    BusTypeSsa,
    BusTypeFibre,
    BusTypeUsb,  ; 目标总线类型
    BusTypeRAID,
    BusTypeiScsi,
    BusTypeSas,
    BusTypeSata,
    BusTypeSd,
    BusTypeMmc,
    BusTypeVirtual,
    BusTypeFileBackedVirtual,
    BusTypeSpaces,
    BusTypeNvme,
    BusTypeSCM,
    BusTypeUfs,
    BusTypeMax
}

struct STORAGE_PROPERTY_QUERY
    DWORD PropertyId
    DWORD QueryType
    BYTE AdditionalParameters[1]
ends

struct STORAGE_DEVICE_DESCRIPTOR
    DWORD Version
    DWORD Size
    BYTE DeviceType
    BYTE DeviceTypeModifier
    BOOL RemovableMedia
    BOOL CommandQueueing
    DWORD VendorIdOffset
    DWORD ProductIdOffset
    DWORD ProductRevisionOffset
    DWORD SerialNumberOffset
    DWORD BusType
    DWORD RawPropertiesLength
    BYTE RawDeviceProperties[1]
ends

Function EnumUSBDrives
    Push $R0
    Push $R1
    Push $R2
    Push $R3
    Push $R4
    Push $R5
    Push $R6
    Push $R7

    ; 枚举物理磁盘,从PhysicalDrive0开始
    StrCpy $R0 0
    loop_disk:
        ; 构造物理磁盘路径
        StrCpy $R1 "\\.\PhysicalDrive"
        StrCat $R1 $R0

        ; 打开物理磁盘(需管理员权限)
        System::Call 'kernel32::CreateFile(t "$R1", i 0x80000000, i 3, i 0, i 3, i 0x80, i 0) p.r2'
        IntCmp $2 -1 end_loop_disk  ; 打开失败,终止枚举

        ; 初始化查询结构
        System::Call '(i ${StorageDeviceProperty}, i ${PropertyStandardQuery}, i 0) .r3'

        ; 先获取所需缓冲区大小
        System::Call 'kernel32::DeviceIoControl(p $2, i ${IOCTL_STORAGE_QUERY_PROPERTY}, p $3, i ${STORAGE_PROPERTY_QUERY_SIZE}, p 0, i 0, *i.r4, i 0) i.r5'
        IntCmp $5 0 skip_disk  ; 调用失败,跳过当前磁盘

        ; 分配缓冲区
        System::Call 'kernel32::LocalAlloc(i 0, i $4) p.r6'
        System::Call 'kernel32::ZeroMemory(p $6, i $4)'

        ; 获取设备描述符
        System::Call 'kernel32::DeviceIoControl(p $2, i ${IOCTL_STORAGE_QUERY_PROPERTY}, p $3, i ${STORAGE_PROPERTY_QUERY_SIZE}, p $6, i $4, *i.r4, i 0) i.r5'
        IntCmp $5 0 free_buffer skip_disk

        ; 读取总线类型
        System::Call '*$6(i, i, i, i, i, i, i, i, i, i.r7, ...)'
        IntCmp $7 ${BusTypeUsb} is_usb skip_disk

        is_usb:
            DetailPrint "USB物理磁盘: PhysicalDrive$R0"
            ; 如需关联逻辑盘符,可扩展调用FindFirstVolumeW、GetVolumeDiskExtents等API继续处理

        skip_disk:
            free_buffer:
                System::Call 'kernel32::LocalFree(p $6)'
            System::Call 'kernel32::CloseHandle(p $2)'
            IntOp $R0 $R0 + 1
            Goto loop_disk
    end_loop_disk:

    Pop $R7
    Pop $R6
    Pop $R5
    Pop $R4
    Pop $R3
    Pop $R2
    Pop $R1
    Pop $R0
FunctionEnd

; 安装流程中调用枚举函数
RequestExecutionLevel admin
Section "枚举USB驱动器"
    Call EnumUSBDrives
SectionEnd

关键说明

  • 权限要求:访问物理磁盘必须拥有管理员权限,脚本开头需添加RequestExecutionLevel admin声明
  • 兼容性:所有用到的API均支持Windows XP及以上系统,无需依赖PowerShell或高版本系统特性
  • 无盘符设备:代码已能识别无盘符的USB物理磁盘,如需获取卷详细信息,可扩展相关API调用
  • 不可移动USB硬盘:通过BusType=USB筛选,不受设备是否标记为"可移动存储"的限制,能精准识别外置USB硬盘

内容的提问来源于stack exchange,提问作者Jiri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 02:42:18