首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在Windows上获取卷大小

在Windows上获取卷大小
EN

Stack Overflow用户
提问于 2013-11-07 00:49:05
回答 1查看 5.4K关注 0票数 10

我正在编写一个库来提取有关Windows系统(XP或更高版本)上的物理磁盘、分区和卷的信息。

我在试着获得一卷容量。以下是我所知道的方法以及每种方法失败的原因:

奇怪的是,来自FSCTL_GET_VOLUME_BITMAP和WMI的CIM_LogicalDisk.Size属性的集群数量是一致的,它们都比来自IOCTL_DISK_GET_LENGTH_INFO的值小4096字节。

获得容量的正确方法是什么?因为所有其他查询都是在没有管理员访问的情况下工作的,所以我也在寻找一种最小特权的解决方案。

EN

回答 1

Stack Overflow用户

发布于 2016-08-09 16:23:27

你到底想要什么?

  • 1)物理磁盘容量 或
  • 2)磁盘上分区的容量 或
  • 3)分区文件系统的容量

物理磁盘有PDO,因为disk.sys创建并附加了FDO (\Device\Harddisk<I>\DR0 -名称或\Device\Harddisk<I>\Partition0 -符号链接,其中i磁盘号为0,1,2.)

对于物理磁盘上的每个分区,disk.sys创建PDO (\Device\Harddisk<I>\Partition<J> - (J in {1,2,3.})-到某些\Device\HarddiskVolume<X>的符号链接)

1)获得物理磁盘容量的方法有几种:

  • a)

使用\Device\Harddisk<I>\Partition<J>打开任何一个(FILE_READ_ACCESS | FILE_WRITE_ACCESS)设备( {0,1,…}中的J-所以磁盘FDO或任何分区PDO),用SCSIOP_READ_CAPACITY和/或SCSIOP_READ_CAPACITY16发送直接,我们得到SCSIOP_READ_CAPACITYSCSIOP_READ_CAPACITY16结构。

代码语言:javascript
运行
复制
READ_CAPACITY_DATA_EX rcd;
SCSI_PASS_THROUGH_DIRECT sptd = {
    sizeof(sptd), 0, 0, 0, 0, CDB12GENERIC_LENGTH, 0, SCSI_IOCTL_DATA_IN, 
    sizeof(rcd), 1, &rcd, 0, {SCSIOP_READ_CAPACITY16}
};

if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_SCSI_PASS_THROUGH_DIRECT,
    &sptd, sizeof(sptd), &sptd, sizeof(sptd)))
{
    DbgPrint("---- SCSIOP_READ_CAPACITY16 ----\n");
    rcd.BytesPerBlock = _byteswap_ulong(rcd.BytesPerBlock);
    rcd.LogicalBlockAddress.QuadPart = _byteswap_uint64(rcd.LogicalBlockAddress.QuadPart) + 1;
    DbgPrint("%I64x %x\n", rcd.LogicalBlockAddress, rcd.BytesPerBlock);
    rcd.LogicalBlockAddress.QuadPart *= rcd.BytesPerBlock;
    DbgPrint("%I64x %I64u\n", rcd.LogicalBlockAddress.QuadPart, rcd.LogicalBlockAddress.QuadPart);
}

代码语言:javascript
运行
复制
    READ_CAPACITY_DATA rcd;
    SCSI_PASS_THROUGH_DIRECT sptd = {
        sizeof(sptd), 0, 0, 0, 0, CDB10GENERIC_LENGTH, 0, SCSI_IOCTL_DATA_IN, 
        sizeof(rcd), 1, &rcd, 0, {SCSIOP_READ_CAPACITY}
    };

    if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_SCSI_PASS_THROUGH_DIRECT,
        &sptd, sizeof(sptd), &sptd, sizeof(sptd)))
    {
        DbgPrint("---- SCSIOP_READ_CAPACITY ----\n");
        rcd.BytesPerBlock = _byteswap_ulong(rcd.BytesPerBlock);
        rcd.LogicalBlockAddress = _byteswap_ulong(rcd.LogicalBlockAddress) + 1;
        DbgPrint("%x %x\n", rcd.LogicalBlockAddress, rcd.BytesPerBlock);
        ULARGE_INTEGER u = {rcd.LogicalBlockAddress};
        u.QuadPart *= rcd.BytesPerBlock;
        DbgPrint("%I64x %I64u\n", u.QuadPart, u.QuadPart);
    }
  • b)

使用\Device\Harddisk<I>\Partition<J>打开任何FILE_READ_ACCESS设备并发送容量 --此请求处理classpnp.sys内的ClassReadDriveCapacity,将SCSI请求(SCSIOP_READ_CAPACITY)发送到磁盘PDO。这种方式在XP上不起作用。

代码语言:javascript
运行
复制
STORAGE_READ_CAPACITY sc;
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_STORAGE_READ_CAPACITY, 0, 0, &sc, sizeof(sc)))
{
    DbgPrint("---- IOCTL_STORAGE_READ_CAPACITY ----\n");
    DbgPrint("%I64x %I64x %x \n", sc.DiskLength.QuadPart, sc.NumberOfBlocks.QuadPart, sc.BlockLength);
    sc.NumberOfBlocks.QuadPart *= sc.BlockLength;
    DbgPrint("%I64x %I64u\n", sc.NumberOfBlocks.QuadPart, sc.NumberOfBlocks.QuadPart);
}
  • c)

打开任何具有任何访问权限的\Device\Harddisk<I>\Partition<J>,发送例如并使用DISK_GEOMETRY_EX.DiskSize。这是最好的办法。不需要任何权限并在XP上工作。

代码语言:javascript
运行
复制
DISK_GEOMETRY_EX GeometryEx;
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, 0, 0, &GeometryEx, sizeof(GeometryEx)))
{
    DbgPrint("---- IOCTL_DISK_GET_DRIVE_GEOMETRY ----\n");

    ULONG BytesPerCylinder = GeometryEx.Geometry.TracksPerCylinder * GeometryEx.Geometry.SectorsPerTrack * GeometryEx.Geometry.BytesPerSector;

    DbgPrint("%I64x == %I64x\n", GeometryEx.Geometry.Cylinders.QuadPart, GeometryEx.DiskSize.QuadPart / BytesPerCylinder);
    DbgPrint("%I64x <= %I64x\n", GeometryEx.Geometry.Cylinders.QuadPart * BytesPerCylinder, GeometryEx.DiskSize.QuadPart);
}
  • d)

\Device\Harddisk<I>\Partition0\Device\Harddisk<I>\Dr0打开FILE_READ_ACCESS并使用信息

  • 2)

要获得磁盘打开的\Device\Harddisk<I>\Partition<J>上分区的容量(其中J在{1,2.}中),或者如果X字母分配给分区- \GLOBAL??\X:并使用信息。再次需要FILE_READ_ACCESS

代码语言:javascript
运行
复制
GET_LENGTH_INFORMATION gli;
if (0 <= NtDeviceIoControlFile(hFile, 0, 0, 0, &iosb, IOCTL_DISK_GET_LENGTH_INFO, 0, 0, &gli, sizeof(gli)))
{
    DbgPrint("---- IOCTL_DISK_GET_LENGTH_INFO ----\n");
    DbgPrint("%I64x %I64u\n", gli.Length.QuadPart, gli.Length.QuadPart);
}
  • 3)

要获得分区上文件系统的容量-打开任意文件(例如\GLOBAL??\X:\)并使用NtQueryVolumeInformationFile

代码语言:javascript
运行
复制
FILE_FS_SIZE_INFORMATION fsi;
if (0 <= NtOpenFile(&hFile, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_VALID_FLAGS, FILE_OPEN_FOR_FREE_SPACE_QUERY|FILE_SYNCHRONOUS_IO_NONALERT))
{
    if (0 <= NtQueryVolumeInformationFile(hFile, &iosb, &fsi, sizeof(fsi), FileFsSizeInformation))
    {
        DbgPrint("%I64x %x %x\n", fsi.TotalAllocationUnits.QuadPart, fsi.SectorsPerAllocationUnit, fsi.BytesPerSector);
        fsi.TotalAllocationUnits.QuadPart *= fsi.SectorsPerAllocationUnit * fsi.BytesPerSector;
        DbgPrint("%I64x %I64u\n", fsi.TotalAllocationUnits.QuadPart, fsi.TotalAllocationUnits.QuadPart);
    }
    NtClose(hFile);
}

或者使用GetDiskFreeSpaceEx -在内部它也调用NtQueryVolumeInformationFile( FileFsSizeInformation),但使用标志FILE_DIRECTORY_FILE,因此作为输入参数,您只能使用目录。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19825910

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档