首页查找驱动Windows 日常使用数据挖掘与人工智能搜索引擎与SEO技术备忘录站长随笔

利用Windows API获取硬盘参数和物理序列号以及SMART信息

2019-07-30 Windows API 浏览次数:5537
 
要获取硬盘信息,必须向硬盘发送一些机器指令,才会有信息返回。而Windows提供了一个API函数,可以向多种硬件设备发送机器指令,这个函数就是:DeviceIoControl

函数声明格式:

BOOL DeviceIoControl(HANDLE hDevice,DWORD dwIoControlCode,LPVOID lpInBuffer,DWORD nInBufferSize,LPVOID lpOutBuffer,DWORD nOutBufferSize,LPDWORD lpBytesReturned,LPOVERLAPPED lpOverlapped);

MSDN对这个函数的介绍:https://docs.microsoft.com/zh-cn/windows/win32/api/ioapiset/nf-ioapiset-deviceiocontrol

DeviceIoControl参数介绍:

hDevice: 调用CreateFile,将得到的句柄传入此参数,使用CreateFile必须传入以下参数('\\.\PhysicalDriveN', GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); 其中:\\.\PhysicalDrive后面的N是硬盘索引号,从零开始,例如:\\.\PhysicalDrive0。

dwIoControlCode:机器指令,有多种机器指令可选,具体参考MSDN,与磁盘有关的机器码指令在这里:https://docs.microsoft.com/zh-cn/windows/win32/fileio/disk-management-control-codes

lpInBuffer:输入参数内存区,具体需要输入什么样的参数,根据dwIoControlCode参数值而定,不同的机器指令有不同的要求

nInBufferSize:lpInBuffer参数内存区的大小

lpOutBuffer:接收返回信息的内存区,具体会返回什么数据格式的信息,根据dwIoControlCode参数值而定,不同的机器指令会返回各种不同数据格式的信息

nOutBufferSize:lpOutBuffer参数内存区的大小

lpBytesReturned:函数执行成功后,lpOutBuffer参数内存区里的数据大小

LPOVERLAPPED:一般很少用到,都填写NULL


1 使用机器码(指令 IOCTL_DISK_GET_DRIVE_GEOMETRY_EX)获取物理磁盘的工作参数:类型,柱面数,每个柱面的轨道数,每个轨道的扇区数和每个扇区的字节数。


DWORD iDrive = 0; // 系统上的硬盘设备索引号,从0开始
char strDriverIndx[128] = { 0 };
sprintf_s(strDriverIndx, 127, "\\\\.\\PhysicalDrive%d", iDrive);
HANDLE hPhysicalDriveIOCTL = CreateFileA(strDriverIndx, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hPhysicalDriveIOCTL != INVALID_HANDLE_VALUE){
DWORD lpOutBufferMaxLen=1024*1024*3;
char* lpOutBuffer=new char[lpOutBufferMaxLen];
DWORD BytesReturned = 0;
memset(lpOutBuffer,0,lpOutBufferMaxLen);
BOOL cs = DeviceIoControl(hPhysicalDriveIOCTL,IOCTL_DISK_GET_DRIVE_GEOMETRY_EX
,0,0,lpOutBuffer,lpOutBufferMaxLen,&BytesReturned,0);
if (cs && (BytesReturned > 0)){
// 由于使用了 IOCTL_DISK_GET_DRIVE_GEOMETRY_EX 指令,所以输出缓冲区里的数据格式为DISK_GEOMETRY_EX
DISK_GEOMETRY_EX* diskgeometry=(DISK_GEOMETRY_EX*)lpOutBuffer;
// 磁盘大小
unsigned __int64 DiskSize = diskgeometry->Geometry.Cylinders.QuadPart * diskgeometry->Geometry.TracksPerCylinder
* diskgeometry->Geometry.SectorsPerTrack * diskgeometry->Geometry.BytesPerSector;

// 磁盘类型 diskgeometry->Geometry.MediaType,值:FixedMedia是磁盘;RemovableMedia是移动磁盘,或U盘光盘之类的
可参考MSDN:MEDIA_TYPE
// 柱面数 diskgeometry->Geometry.Cylinders
// 每个柱面的轨道数 diskgeometry->Geometry.TracksPerCylinder
// 每个磁道的扇区数 diskgeometry->Geometry.SectorsPerTrack
// 每个扇区的字节数 diskgeometry->Geometry.BytesPerSector
}

delete[] lpOutBuffer;
CloseHandle(hPhysicalDriveIOCTL);
}

本机运行之后的结果:


2 获取获取磁盘制造信息(硬盘类型,总线类型,厂商,产品型号,序列号)

使用的机器码为:IOCTL_STORAGE_QUERY_PROPERTY
char* wbuf=new char[wbufmaxlen];
memset(wbuf,0,wbufmaxlen);
OutBuf也一样,是个内存块。
DeviceIoControl('CreateFile返回的句柄',IOCTL_STORAGE_QUERY_PROPERTY,wbuf,wbufmaxlen,OutBuf,OutBufMaxLen,&OutBufLen,0);
返回的数据结构为:PSTORAGE_DEVICE_DESCRIPTOR(OutBuf);

3 获取硬盘基本信息,这个信息很强大,包含了很多数据,包括前面两种方法获得的数据,返回的是512个字节的数据。

使用机器码:SMART_RCV_DRIVE_DATA

输入参数的结构为:SENDCMDINPARAMS
char* wbuf=new char[wbufmaxlen];
memset(wbuf,0,wbufmaxlen);
lpSendCmdInParams=(PSENDCMDINPARAMS)wbuf;
lpSendCmdInParams->cBufferSize=OutBufMaxLen; // 这里填的是输出缓冲区的大小
lpSendCmdInParams->irDriveRegs.bSectorCountReg=1;
DWORD D=iDrive & 1;
if (D > 0){lpSendCmdInParams->irDriveRegs.bDriveHeadReg=0xB0;} else {lpSendCmdInParams->irDriveRegs.bDriveHeadReg=0xA0;}
lpSendCmdInParams->irDriveRegs.bDriveHeadReg= $A0 | ((iDrive &1) << 4);
lpSendCmdInParams->irDriveRegs.bCommandReg=ID_CMD; // 如果驱动器类型是 ATAPI ,就用这个指令,其他类型,一律用 ID_CMD
DeviceIoControl('CreateFile返回的句柄',SMART_RCV_DRIVE_DATA,wbuf,wbufmaxlen,OutBuf,OutBufMaxLen,&OutBufLen,0);
返回的数据结构是:PSENDCMDOUTPARAMS(OutBuf); // SENDCMDOUTPARAMS 结构里的bBuffer指针,就是指向IDINFO结构的首地址

PSENDCMDOUTPARAMS lpSendCmdOutParams=PSENDCMDOUTPARAMS(OutBuf);
PIDINFO lpPIDINFO=(PIDINFO)lpSendCmdOutParams->bBuffer;


typedef struct _IDINFO
{
USHORT wGenConfig; // WORD 0: 基本信息字
USHORT wNumCyls; // WORD 1: 柱面数
USHORT wReserved2; // WORD 2: 保留
USHORT wNumHeads; // WORD 3: 磁头数
USHORT wReserved4; // WORD 4: 保留
USHORT wReserved5; // WORD 5: 保留
USHORT wNumSectorsPerTrack; // WORD 6: 每磁道扇区数
USHORT wVendorUnique[3]; // WORD 7-9: 厂家设定值
CHAR sSerialNumber[20]; // WORD 10-19:序列号
USHORT wBufferType; // WORD 20: 缓冲类型
USHORT wBufferSize; // WORD 21: 缓冲大小
USHORT wECCSize; // WORD 22: ECC校验大小
CHAR sFirmwareRev[8]; // WORD 23-26: 固件版本
CHAR sModelNumber[40]; // WORD 27-46: 内部型号
USHORT wMoreVendorUnique; // WORD 47: 厂家设定值
USHORT wReserved48; // WORD 48: 保留
struct {
USHORT reserved1 : 8;
USHORT DMA : 1; // 1=支持DMA
USHORT LBA : 1; // 1=支持LBA
USHORT DisIORDY : 1; // 1=可不使用IORDY
USHORT IORDY : 1; // 1=支持IORDY
USHORT SoftReset : 1; // 1=需要ATA软启动
USHORT Overlap : 1; // 1=支持重叠操作
USHORT Queue : 1; // 1=支持命令队列
USHORT InlDMA : 1; // 1=支持交叉存取DMA
} wCapabilities; // WORD 49: 一般能力
USHORT wReserved1; // WORD 50: 保留
USHORT wPIOTiming; // WORD 51: PIO时序
USHORT wDMATiming; // WORD 52: DMA时序
struct {
USHORT CHSNumber : 1; // 1=WORD 54-58有效
USHORT CycleNumber : 1; // 1=WORD 64-70有效
USHORT UnltraDMA : 1; // 1=WORD 88有效
USHORT reserved : 13;
} wFieldValidity; // WORD 53: 后续字段有效性标志
USHORT wNumCurCyls; // WORD 54: CHS可寻址的柱面数
USHORT wNumCurHeads; // WORD 55: CHS可寻址的磁头数
USHORT wNumCurSectorsPerTrack; // WORD 56: CHS可寻址每磁道扇区数
USHORT wCurSectorsLow; // WORD 57: CHS可寻址的扇区数低位字
USHORT wCurSectorsHigh; // WORD 58: CHS可寻址的扇区数高位字
struct {
USHORT CurNumber : 8; // 当前一次性可读写扇区数
USHORT Multi : 1; // 1=已选择多扇区读写
USHORT reserved1 : 7;
} wMultSectorStuff; // WORD 59: 多扇区读写设定
ULONG dwTotalSectors; // WORD 60-61: LBA可寻址的扇区数
USHORT wSingleWordDMA; // WORD 62: 单字节DMA支持能力
struct {
USHORT Mode0 : 1; // 1=支持模式0 (4.17Mb/s)
USHORT Mode1 : 1; // 1=支持模式1 (13.3Mb/s)
USHORT Mode2 : 1; // 1=支持模式2 (16.7Mb/s)
USHORT Reserved1 : 5;
USHORT Mode0Sel : 1; // 1=已选择模式0
USHORT Mode1Sel : 1; // 1=已选择模式1
USHORT Mode2Sel : 1; // 1=已选择模式2
USHORT Reserved2 : 5;
} wMultiWordDMA; // WORD 63: 多字节DMA支持能力
struct {
USHORT AdvPOIModes : 8; // 支持高级POI模式数
USHORT reserved : 8;
} wPIOCapacity; // WORD 64: 高级PIO支持能力
USHORT wMinMultiWordDMACycle; // WORD 65: 多字节DMA传输周期的最小值
USHORT wRecMultiWordDMACycle; // WORD 66: 多字节DMA传输周期的建议值
USHORT wMinPIONoFlowCycle; // WORD 67: 无流控制时PIO传输周期的最小值
USHORT wMinPOIFlowCycle; // WORD 68: 有流控制时PIO传输周期的最小值
USHORT wReserved69[11]; // WORD 69-79: 保留
struct {
USHORT Reserved1 : 1;
USHORT ATA1 : 1; // 1=支持ATA-1
USHORT ATA2 : 1; // 1=支持ATA-2
USHORT ATA3 : 1; // 1=支持ATA-3
USHORT ATA4 : 1; // 1=支持ATA/ATAPI-4
USHORT ATA5 : 1; // 1=支持ATA/ATAPI-5
USHORT ATA6 : 1; // 1=支持ATA/ATAPI-6
USHORT ATA7 : 1; // 1=支持ATA/ATAPI-7
USHORT ATA8 : 1; // 1=支持ATA/ATAPI-8
USHORT ATA9 : 1; // 1=支持ATA/ATAPI-9
USHORT ATA10 : 1; // 1=支持ATA/ATAPI-10
USHORT ATA11 : 1; // 1=支持ATA/ATAPI-11
USHORT ATA12 : 1; // 1=支持ATA/ATAPI-12
USHORT ATA13 : 1; // 1=支持ATA/ATAPI-13
USHORT ATA14 : 1; // 1=支持ATA/ATAPI-14
USHORT Reserved2 : 1;
} wMajorVersion; // WORD 80: 主版本
USHORT wMinorVersion; // WORD 81: 副版本
USHORT wReserved82[6]; // WORD 82-87: 保留
struct {
USHORT Mode0 : 1; // 1=支持模式0 (16.7Mb/s)
USHORT Mode1 : 1; // 1=支持模式1 (25Mb/s)
USHORT Mode2 : 1; // 1=支持模式2 (33Mb/s)
USHORT Mode3 : 1; // 1=支持模式3 (44Mb/s)
USHORT Mode4 : 1; // 1=支持模式4 (66Mb/s)
USHORT Mode5 : 1; // 1=支持模式5 (100Mb/s)
USHORT Mode6 : 1; // 1=支持模式6 (133Mb/s)
USHORT Mode7 : 1; // 1=支持模式7 (166Mb/s) ???
USHORT Mode0Sel : 1; // 1=已选择模式0
USHORT Mode1Sel : 1; // 1=已选择模式1
USHORT Mode2Sel : 1; // 1=已选择模式2
USHORT Mode3Sel : 1; // 1=已选择模式3
USHORT Mode4Sel : 1; // 1=已选择模式4
USHORT Mode5Sel : 1; // 1=已选择模式5
USHORT Mode6Sel : 1; // 1=已选择模式6
USHORT Mode7Sel : 1; // 1=已选择模式7
} wUltraDMA; // WORD 88: Ultra DMA支持能力
USHORT wReserved89[167]; // WORD 89-255
} IDINFO, *PIDINFO;

4 获取硬盘 SMART 运行信息和阈值信息,得到的数据是512个字节的二进制数据,需要自己解释。

使用机器指令:SMART_RCV_DRIVE_DATA

// 第一步获得的数据是SMART的运行值
输入的数据结构为:SENDCMDINPARAMS
char* wbuf=new char[wbufmaxlen];
memset(wbuf,0,wbufmaxlen);
lpSendCmdInParams=(PSENDCMDINPARAMS)wbuf;
lpSendCmdInParams->cBufferSize=sizeof(SENDCMDINPARAMS);
lpSendCmdInParams->irDriveRegs.bFeaturesReg=READ_ATTRIBUTES;
lpSendCmdInParams->irDriveRegs.bCommandReg=SMART_CMD;
lpSendCmdInParams->irDriveRegs.bSectorCountReg=1;
lpSendCmdInParams->irDriveRegs.bSectorNumberReg=1;
lpSendCmdInParams->irDriveRegs.bCylLowReg=SMART_CYL_LOW;
lpSendCmdInParams->irDriveRegs.bCylHighReg=SMART_CYL_HI;
lpSendCmdInParams->irDriveRegs.bDriveHeadReg=$A0;
DeviceIoControl('CreateFile返回的句柄',SMART_RCV_DRIVE_DATA,wbuf,wbufmaxlen,OutBuf,OutBufMaxLen,&OutBufLen,0);
返回的数据结构是:PSENDCMDOUTPARAMS(OutBuf);

// 第二步获得的数据是SMART的阈值
lpSendCmdInParams->irDriveRegs.bFeaturesReg=READ_THRESHOLDS;
DeviceIoControl('CreateFile返回的句柄',SMART_RCV_DRIVE_DATA,wbuf,wbufmaxlen,OutBuf,OutBufMaxLen,&OutBufLen,0);
返回的数据结构是:PSENDCMDOUTPARAMS(OutBuf);
留言

有啥想说的就说吧,有啥想问的就问吧
Good good study, day day up!

昵称

Email (填它做啥?国内不兴这玩意,但程序代码里有,我懒得删...)

    ToolBar:

    正在上传图片,请稍等...   

内容  (如果可以的话,最好有相关问题的几张图,特别是出现了错误的时候,当时弹出的错误消息,或者对话框之类的,截图传上来看看吧,这样才好知道具体情况)

查看 HTML 代码(只读模式), 点击返回编辑.

 
最新文章
 
求助
2019 - 2024 mypcrun.com
桂ICP备19002156号-1桂公网安备 45070202000667号
这回把网站设计得那么漂亮,这下子不会被人笑了吧。