WMI:Win32_DiskDrive 获取物理磁盘驱动器上的总扇区数

5
我尝试使用Win32_DiskDrive获取磁盘的最大总扇区,但所有返回值都是不正确的。我使用HxD这个程序返回了准确的值。我尝试在Linux中使用fdisk获取总扇区,它也返回了准确的值。
我注意到Win32_DiskDrive MSDN中有一个注释,如下所示:
“此属性的值是通过BIOS中断13h的扩展函数获得的。如果驱动器使用翻译方案来支持高容量磁盘大小,则该值可能不准确。请咨询制造商以获取准确的驱动器规格。”
但我不明白这是什么意思?如何解决这个问题?
更新1:
这是我的Python脚本的代码片段。

必需品:Python, PyWin32, WMI

import wmi

c = wmi.WMI()
for diskDrive in c.query("SELECT * FROM Win32_DiskDrive"):
    print diskDrive.Name, "\nTotal Sector: ", diskDrive.TotalSectors

更新2:

根据要求,以下是使用HxD检测到的WMI总扇区的代码片段。

  • WMI:625137345(顶部)
  • HxD:625142448(底部)

Sector Difference Screenshot

更新3:

如果有人感兴趣,也可以在自己的计算机上尝试一下,看看Win32_DiskDrive是否报告准确的结果。我在许多其他计算机上(WinXP和7)以及其他存储设备(硬盘、闪存驱动器等)上尝试过,但所有结果都不准确。

请安装PythonPyWin32WMI进行尝试

非常感谢


你所提到的注释没有提及Win32_DiskDrive WMI类的BytesPerSector属性,那么你从哪里获取这个信息呢? - RRUZ
@RRUZ 抱歉,我刚刚修改了我的问题,我的意思不是扇区大小,而是“总扇区数”。 - Yeo
1
你的代码在哪里?你想在哪些平台上实现这个功能? - Chibueze Opata
@ChibuezeOpata:我更新了我的问题,提供了一些代码片段。 - Yeo
3个回答

3
WMI报告的磁盘大小比实际小几MB。我认为这与Windows处理驱动器的柱面/磁头/扇区有关。
我的解决方案是读取超出报告的驱动器大小,直到出现错误:
import wmi
disks = wmi.WMI().Win32_DiskDrive(MediaType="Removable Media")
for disk in disks:
    disk_size = int(disk.size)
    sector_size = disk.BytesPerSector
    print(disk.name, "reported size:", disk_size)
    with open(disk.name, "rb") as f:
        f.seek(disk_size)
        while True:
            try:
                f.read(sector_size)
                disk_size += sector_size
            except PermissionError:
                break
    print(disk.name, "readable size:", disk_size)

我对两张不同的32GB SD卡得到了以下结果:
\\.\PHYSICALDRIVE2 reported size: 31683778560
\\.\PHYSICALDRIVE2 readable size: 31691110400
\\.\PHYSICALDRIVE3 reported size: 31437020160
\\.\PHYSICALDRIVE3 readable size: 31439453184

然而,实际驱动器实际上有额外的1024到2048个字节,我们仍然无法读取,我不确定如何获取它们。但是,这比之前丢失了几MB要好。

编辑:看起来缓冲导致了读取最后几个字节的问题。如果我使用open(disk.name, "rb", buffering=0),我可以读取剩余的字节。然而,这非常慢(每秒约1MB,一个驱动器需要大约7秒)。可能有一个很好的混合方法,您只需要在最后几个字节中使用buffering = 0,其余时间使用默认的buffering。

\\.\PHYSICALDRIVE2 reported size: 31683778560
\\.\PHYSICALDRIVE2 readable size: 31691112448 (with buffering=0)
\\.\PHYSICALDRIVE14 reported size: 31437020160
\\.\PHYSICALDRIVE14 readable size: 31439454208 (with buffering=0)

编辑2:您可以使用read1获取最后几个字节,而不需要使用buffering=0打开文件。因此,要获取实际磁盘大小,可以执行以下操作:
reported_size = disk.size
f.seek(reported_size)
while True:
    try:  # Read beyond the reported size
        f.read(sector_size)
    except PermissionError:
        easily_readable_size = f.tell()
        try:  # Get the last few bytes using read1 (unbuffered)
            for i in range(128):  # Test up to this many additional sectors
                f.read1(self.sector_size)
        except PermissionError:
            actual_size = f.tell()
            break

注意,我认为您可能不总能够读取到“easily_readable_size”,因为内部缓冲区的对齐方式可能并不总是相同的。我将该值减少了“io.DEFAULT_BUFFER_SIZE”以增加一些安全性。然后,我用自己的函数覆盖了“f.read”,这个函数结合以上方法透明地正确读取整个磁盘。这样,“f.read”就可以按照您最初期望的方式工作了。

1

我在使用用户user3268002的读取测试解决方案时遇到了问题。使用工作正常的USB闪存驱动器会导致冻结一分钟,还会使其他应用程序无法访问驱动器列表,在此期间甚至有时会导致驱动器变得不可访问,直到重新插入。根据初始查找位置,报告的大小也是不正确的,因为第一个read()调用愉快地读取了几个扇区而没有异常,导致报告太多字节(扇区)。但它可以与普通硬盘一起使用。

以下代码始终为我提供了所有已测试驱动器的正确大小,如HxD所报告。

import struct
import win32file  #pip install pywin32
import winioctlcon #pip install pywin32

f = win32file.CreateFile('\\\\.\\PHYSICALDRIVE0', win32file.GENERIC_READ, 0, None, win32file.OPEN_EXISTING, win32file.FILE_ATTRIBUTE_NORMAL, 0)
size = win32file.DeviceIoControl(f, winioctlcon.IOCTL_DISK_GET_LENGTH_INFO, None, 512, None)  #returns bytes
size = struct.unpack('q', size)[0]  #convert 64 bit int from bytes to int -> first element of returned tuple
print(size)
f.close()

1
欢迎来到StackOverflow。虽然这段代码可能回答了问题,但是提供有关它解决问题的如何和/或为什么的附加上下文会提高答案的长期价值。 - Sven Eberth
我会使用 win32file.GENERIC_READ, win32file.FILE_SHARE_READ | win32file.FILE_SHARE_WRITE, None 代替 win32file.GENERIC_READ, 0, None。否则其他进程无法同时访问 '\\.\PHYSICALDRIVE0' 对象。此外,从我的实验结果来看,如果 Python 在关闭句柄之前退出,则由于某种原因,您将无法在另一个 Python 进程中再次打开该句柄。 - Roland Pihlakas

0

你说这段代码对于特定的硬盘不起作用?你能告诉我们这个硬盘的详细信息以及你是如何知道它不正确的吗?

然而,尝试使用纯WinAPI方法。可以使用DeviceIoControl来轻松完成此操作。在cpp中查看完整的代码清单

我知道有一种方法可以在Python中编写C++,祝你好运 :)


这段代码适用于任何硬盘,只是结果比HxD应用程序的结果少。HxD检测到的扇区比WinAPI检测到的多。最初,我使用C编程语言来执行DeviceIoControl,但结果也较少。然后我尝试使用Python的WMI,结果还是较少。我尝试在另一台具有不同磁盘的机器上运行,使用此WinAPI的结果始终少于HxD检测到的结果。 - Yeo
据我所知,WINAPI应该提供最佳的准确性。其他可能性是您的磁盘出现问题或者HxD有误。您能在这里发布您得到的不同值大小吗? - Chibueze Opata
我刚刚在问题更新2中上传了截图。你是正确的,WINAPI总是提供最好的准确性。但是,在Win32_DiskDrive中,有一个注释说:如果驱动器使用翻译方案支持高容量磁盘大小,则该值可能不准确。我不太明白这个注释的意思。如何解决这个问题?为什么HxD可以检测到正确的值.. - Yeo
你没有添加 WINAPI 返回什么? - Chibueze Opata
Win32_DiskDrive返回有关DiskDrive属性的所有信息。因此,我只需访问其属性(SectorSize)。如果我误解了您所说的WINAPI返回值含义,那么请见谅。 - Yeo
我的意思是你应该使用上面实现DeviceIoControl的代码示例。实现后,在此处发布结果。 - Chibueze Opata

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接