【问题标题】:Python and GetVolumeInformationW and GetDriveTypeW: list all disks and get disk info and filesystem flagsPython 和 GetVolumeInformationW 和 GetDriveTypeW:列出所有磁盘并获取磁盘信息和文件系统标志
【发布时间】:2021-10-24 01:09:02
【问题描述】:

在 python3 上使用 windows API: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getvolumeinformationw

如何通过GetVolumeInformationW这个函数获取这些值(file_system_flags、max_component_length和serial_number),而不需要安装任何其他外部模块?

import ctypes

kernel32 = ctypes.windll.kernel32
volumeNameBuffer = ctypes.create_unicode_buffer(1024)
fileSystemNameBuffer = ctypes.create_unicode_buffer(1024)
serial_number = None
max_component_length = None
file_system_flags = None

target_disk = 'C:\\'
rc = kernel32.GetVolumeInformationW(
    ctypes.c_wchar_p(target_disk),
    volumeNameBuffer, ctypes.sizeof(volumeNameBuffer),
    serial_number,
    max_component_length,
    file_system_flags, 
    fileSystemNameBuffer, ctypes.sizeof(fileSystemNameBuffer)
)

mount_point = target_disk[:-1]
disk_label = volumeNameBuffer.value
fs_type = fileSystemNameBuffer.value
max_length = max_component_length
flags = file_system_flags
serial = serial_number

print(mount_point, disk_label, fs_type, max_length, flags, serial)

补充: 以及如何将文件系统标志转换为人类可读的格式?

my_disk_flag = 0x3e706ff

hex_flags = [0x00000002, 0x00000001, 0x20000000, 0x00000010, 0x00040000, 0x00000008, 0x00080000, 0x00100000, 0x00020000, 0x00800000, 0x00400000, 0x00010000, 0x01000000, 0x00000080, 0x00000040, 0x00200000, 0x02000000, 0x00000004, 0x00008000, 0x00000020, 0x08000000]

str_flags = ['FILE_CASE_PRESERVED_NAMES', 'FILE_CASE_SENSITIVE_SEARCH', 'FILE_DAX_VOLUME', 'FILE_FILE_COMPRESSION', 'FILE_NAMED_STREAMS', 'FILE_PERSISTENT_ACLS', 'FILE_READ_ONLY_VOLUME', 'FILE_SEQUENTIAL_WRITE_ONCE', 'FILE_SUPPORTS_ENCRYPTION', 'FILE_SUPPORTS_EXTENDED_ATTRIBUTES', 'FILE_SUPPORTS_HARD_LINKS', 'FILE_SUPPORTS_OBJECT_IDS', 'FILE_SUPPORTS_OPEN_BY_FILE_ID', 'FILE_SUPPORTS_REPARSE_POINTS', 'FILE_SUPPORTS_SPARSE_FILES', 'FILE_SUPPORTS_TRANSACTIONS', 'FILE_SUPPORTS_USN_JOURNAL', 'FILE_UNICODE_ON_DISK', 'FILE_VOLUME_IS_COMPRESSED', 'FILE_VOLUME_QUOTAS', 'FILE_SUPPORTS_BLOCK_REFCOUNTING']

【问题讨论】:

    标签: python windows filesystems ctypes flags


    【解决方案1】:

    创建该类型的一个实例并通过引用传递它。它相当于在 C 中声明一个局部变量并传递其地址,例如DWORD flags; 并作为 &flags 作为输出参数传递。

    它还通过声明函数调用的.argtypes.restype 来帮助ctypes 错误检查,这类似于声明C 原型。 ctypes.wintypes 有许多适用于 Windows 的预定义类型。

    我还添加了一个 .errcheck 属性,该属性会根据 GetLastError() 代码自动检查返回值并引发 Windows 异常,以及将标志处理为可读格式的枚举:

    import ctypes as ct
    from ctypes import wintypes as w
    from enum import IntFlag
    
    class FSFlags(IntFlag):
        FILE_CASE_SENSITIVE_SEARCH        = 0x00000001
        FILE_CASE_PRESERVED_NAMES         = 0x00000002
        FILE_UNICODE_ON_DISK              = 0x00000004
        FILE_PERSISTENT_ACLS              = 0x00000008
        FILE_FILE_COMPRESSION             = 0x00000010
        FILE_VOLUME_QUOTAS                = 0x00000020
        FILE_SUPPORTS_SPARSE_FILES        = 0x00000040
        FILE_SUPPORTS_REPARSE_POINTS      = 0x00000080
        FILE_VOLUME_IS_COMPRESSED         = 0x00008000
        FILE_SUPPORTS_OBJECT_IDS          = 0x00010000
        FILE_SUPPORTS_ENCRYPTION          = 0x00020000
        FILE_NAMED_STREAMS                = 0x00040000
        FILE_READ_ONLY_VOLUME             = 0x00080000
        FILE_SEQUENTIAL_WRITE_ONCE        = 0x00100000
        FILE_SUPPORTS_TRANSACTIONS        = 0x00200000
        FILE_SUPPORTS_HARD_LINKS          = 0x00400000
        FILE_SUPPORTS_EXTENDED_ATTRIBUTES = 0x00800000
        FILE_SUPPORTS_OPEN_BY_FILE_ID     = 0x01000000
        FILE_SUPPORTS_USN_JOURNAL         = 0x02000000
        FILE_SUPPORTS_BLOCK_REFCOUNTING   = 0x08000000
        FILE_DAX_VOLUME                   = 0x20000000
    
    def validate(result,func,args):
        if not result:
            raise ct.WinError(ct.get_last_error())
        return None
    
    dll = ct.WinDLL('kernel32',use_last_error=True)
    dll.GetVolumeInformationW.argtypes = w.LPCWSTR,w.LPWSTR,w.DWORD,w.LPDWORD,w.LPDWORD,w.LPDWORD,w.LPWSTR,w.DWORD
    dll.GetVolumeInformationW.restype = w.BOOL
    dll.GetVolumeInformationW.errcheck = validate
    
    volumeNameBuffer = ct.create_unicode_buffer(w.MAX_PATH + 1)
    fileSystemNameBuffer = ct.create_unicode_buffer(w.MAX_PATH + 1)
    serial_number = w.DWORD()
    max_component_length = w.DWORD() 
    file_system_flags = w.DWORD()
    
    target_disk = 'c:\\'
    
    dll.GetVolumeInformationW(target_disk,
                              volumeNameBuffer, ct.sizeof(volumeNameBuffer),
                              ct.byref(serial_number),
                              ct.byref(max_component_length),
                              ct.byref(file_system_flags),
                              fileSystemNameBuffer, ct.sizeof(fileSystemNameBuffer))
    
    mount_point = target_disk[:-1]
    disk_label = volumeNameBuffer.value
    fs_type = fileSystemNameBuffer.value
    max_length = max_component_length.value
    flags = FSFlags(file_system_flags.value)
    serial = serial_number.value
    
    print(f'{mount_point=}\n{disk_label=}\n{fs_type=}\n{max_length=}\n{flags=}\n{serial=}')
    

    输出示例:

    mount_point='c:'
    disk_label=''
    fs_type='NTFS'
    max_length=255
    flags=<FSFlags.FILE_SUPPORTS_USN_JOURNAL|FILE_SUPPORTS_OPEN_BY_FILE_ID|FILE_SUPPORTS_EXTENDED_ATTRIBUTES|FILE_SUPPORTS_HARD_LINKS|FILE_SUPPORTS_TRANSACTIONS|FILE_NAMED_STREAMS|FILE_SUPPORTS_ENCRYPTION|FILE_SUPPORTS_OBJECT_IDS|1024|512|FILE_SUPPORTS_REPARSE_POINTS|FILE_SUPPORTS_SPARSE_FILES|FILE_VOLUME_QUOTAS|FILE_FILE_COMPRESSION|FILE_PERSISTENT_ACLS|FILE_UNICODE_ON_DISK|FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH: 65472255>
    serial=3465270344
    

    【讨论】:

    • 感谢您的帮助!以及如何将文件系统标志转换为人类可读的格式?见补。 my_disk_flag = 0x3e706ff
    • @MrkalvinBs 使用enumeration 定义标志位。我使用了IntFlag,因为我的系统上设置了一些未记录的位。
    【解决方案2】:

    为了将来参考,我将在此处留下有关如何列出所有磁盘并获取其类似 fstab 的信息的完整代码

    import ctypes as ct
    import string
    from ctypes import wintypes as w
    from enum import IntFlag
    from pathlib import Path
    
    class FSFlags(IntFlag):
        FILE_CASE_SENSITIVE_SEARCH        = 0x00000001
        FILE_CASE_PRESERVED_NAMES         = 0x00000002
        FILE_UNICODE_ON_DISK              = 0x00000004
        FILE_PERSISTENT_ACLS              = 0x00000008
        FILE_FILE_COMPRESSION             = 0x00000010
        FILE_VOLUME_QUOTAS                = 0x00000020
        FILE_SUPPORTS_SPARSE_FILES        = 0x00000040
        FILE_SUPPORTS_REPARSE_POINTS      = 0x00000080
        FILE_VOLUME_IS_COMPRESSED         = 0x00008000
        FILE_SUPPORTS_OBJECT_IDS          = 0x00010000
        FILE_SUPPORTS_ENCRYPTION          = 0x00020000
        FILE_NAMED_STREAMS                = 0x00040000
        FILE_READ_ONLY_VOLUME             = 0x00080000
        FILE_SEQUENTIAL_WRITE_ONCE        = 0x00100000
        FILE_SUPPORTS_TRANSACTIONS        = 0x00200000
        FILE_SUPPORTS_HARD_LINKS          = 0x00400000
        FILE_SUPPORTS_EXTENDED_ATTRIBUTES = 0x00800000
        FILE_SUPPORTS_OPEN_BY_FILE_ID     = 0x01000000
        FILE_SUPPORTS_USN_JOURNAL         = 0x02000000
        FILE_SUPPORTS_BLOCK_REFCOUNTING   = 0x08000000
        FILE_DAX_VOLUME                   = 0x20000000
    
    def validate(result,func,args):
        if not result:
            raise ct.WinError(ct.get_last_error())
        return None
    
    dll = ct.WinDLL('kernel32',use_last_error=True)
    dll.GetVolumeInformationW.argtypes = w.LPCWSTR,w.LPWSTR,w.DWORD,w.LPDWORD,w.LPDWORD,w.LPDWORD,w.LPWSTR,w.DWORD
    dll.GetVolumeInformationW.restype = w.BOOL
    dll.GetVolumeInformationW.errcheck = validate
    
    volumeNameBuffer = ct.create_unicode_buffer(w.MAX_PATH + 1)
    fileSystemNameBuffer = ct.create_unicode_buffer(w.MAX_PATH + 1)
    serial_number = w.DWORD()
    max_component_length = w.DWORD() 
    file_system_flags = w.DWORD()
    
    lst_available_disks = [f'{d}:\\' for d in string.ascii_uppercase if Path(f'{d}:\\').exists()]
    lst_disk_types = ['DRIVE_UNKNOWN', 'DRIVE_NO_ROOT_DIR', 'DRIVE_REMOVABLE', 'DRIVE_FIXED', 'DRIVE_REMOTE', 'DRIVE_CDROM', 'DRIVE_RAMDISK']
    
    for target_disk in lst_available_disks:
        disk_type_index = dll.GetDriveTypeW(target_disk)
    
        dll.GetVolumeInformationW(target_disk,
                                volumeNameBuffer, ct.sizeof(volumeNameBuffer),
                                ct.byref(serial_number),
                                ct.byref(max_component_length),
                                ct.byref(file_system_flags),
                                fileSystemNameBuffer, ct.sizeof(fileSystemNameBuffer))
    
        mount_point = target_disk
        disk_label = volumeNameBuffer.value
        fs_type = fileSystemNameBuffer.value
        max_length = max_component_length.value
        flags = FSFlags(file_system_flags.value)
        serial = serial_number.value
    
        if 'FILE_READ_ONLY_VOLUME' in str(flags):
            read_write_status = 'ro'
        else:
            read_write_status = 'rw'
    
        extra_tab_label = ''
        if len(disk_label) < 8: extra_tab_label = '\t'
        extra_tab_type = ''
        if len(lst_disk_types[disk_type_index]) < 12: extra_tab_type = '\t'
    
        print(f'{disk_label}{extra_tab_label}\t{mount_point}\t{fs_type}\t{max_length}\t{serial}\t{read_write_status},{lst_disk_types[disk_type_index]}{extra_tab_type}\t{flags=}\n')
    

    输出(只是用零替换磁盘序列):

    Win10           C:\     NTFS    255     0000000000      rw,DRIVE_FIXED          flags=<FSFlags.FILE_SUPPORTS_USN_JOURNAL|FILE_SUPPORTS_OPEN_BY_FILE_ID|FILE_SUPPORTS_EXTENDED_ATTRIBUTES|FILE_SUPPORTS_HARD_LINKS|FILE_SUPPORTS_TRANSACTIONS|FILE_NAMED_STREAMS|FILE_SUPPORTS_ENCRYPTION|FILE_SUPPORTS_OBJECT_IDS|1024|512|FILE_SUPPORTS_REPARSE_POINTS|FILE_SUPPORTS_SPARSE_FILES|FILE_VOLUME_QUOTAS|FILE_FILE_COMPRESSION|FILE_PERSISTENT_ACLS|FILE_UNICODE_ON_DISK|FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH: 65472255>     
    
                    D:\     FAT32   255     0000000000      rw,DRIVE_FIXED          flags=<FSFlags.FILE_SUPPORTS_ENCRYPTION|512|FILE_UNICODE_ON_DISK|FILE_CASE_PRESERVED_NAMES: 131590>
    
    DRV061107       E:\     CDFS    110     0000000000      ro,DRIVE_CDROM          flags=<FSFlags.FILE_SUPPORTS_OPEN_BY_FILE_ID|FILE_READ_ONLY_VOLUME|FILE_UNICODE_ON_DISK|FILE_CASE_SENSITIVE_SEARCH: 17301509>
    
    Ventoy          F:\     exFAT   255     0000000000      rw,DRIVE_REMOVABLE      flags=<FSFlags.FILE_SUPPORTS_ENCRYPTION|512|FILE_UNICODE_ON_DISK|FILE_CASE_PRESERVED_NAMES: 131590>
    
    VTOYEFI         G:\     FAT     255     0000000000      rw,DRIVE_REMOVABLE      flags=<FSFlags.FILE_SUPPORTS_ENCRYPTION|512|FILE_UNICODE_ON_DISK|FILE_CASE_PRESERVED_NAMES: 131590>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-03
      • 1970-01-01
      • 2021-11-10
      • 2022-01-17
      • 2021-03-29
      • 1970-01-01
      相关资源
      最近更新 更多