遇到这个问题的时候首先就将windows的几个直接获取磁盘相关信息的API函数给否决掉了,比如GetDiskType,返回类型只有几个,并且固态硬盘作为新生事物,这个多年前就存在的API应该是不可能有相关信息的。
下面就想到了WMI来获取磁盘的相关信息来判断,WMI获取的磁盘相关信息明显是多了很多,但是仔细到MSDN上查看相关的返回值,发现并没有可以作为判断依据的属性或者。本人也亲自实现了一下,发现果然是没有什么返回值可以给我们作为判断的依据。
下面我们到网上查了下相关的解决办法,里面有人提到,通过判断磁盘的转速来判断磁盘是不是固态硬盘BOOL
下面我通过DeviceIoControl函数获取了文章中所说的转速信息的word217所在的结构体的信息
// 向驱动发“IDENTIFY DEVICE”命令,获得设备信息 // hDevice: 设备句柄 // pIdInfo: 设备信息结构指针
BOOL IdentifyDevice(HANDLE hDevice, PIDINFO pIdInfo)
{
PSENDCMDINPARAMS pSCIP; // 输入数据结构指针
PSENDCMDOUTPARAMS pSCOP; // 输出数据结构指针
DWORD dwOutBytes; // IOCTL输出数据长度
BOOL bResult; // IOCTL返回值 // 申请输入/输出数据结构空间
pSCIP = (PSENDCMDINPARAMS)::GlobalAlloc(LMEM_ZEROINIT, sizeof(SENDCMDINPARAMS) - 1);
pSCOP = (PSENDCMDOUTPARAMS)::GlobalAlloc(LMEM_ZEROINIT, sizeof(SENDCMDOUTPARAMS) + sizeof(IDINFO) - 1); // 指定ATA/ATAPI命令的寄存器值 //
pSCIP->irDriveRegs.bFeaturesReg = 0; //
pSCIP->irDriveRegs.bSectorCountReg = 0; //
pSCIP->irDriveRegs.bSectorNumberReg = 0; //
pSCIP->irDriveRegs.bCylLowReg = 0; //
pSCIP->irDriveRegs.bCylHighReg = 0; //
pSCIP->irDriveRegs.bDriveHeadReg = 0;
pSCIP->irDriveRegs.bCommandReg = IDE_ATA_IDENTIFY; // 指定输入/输出数据缓冲区大小 IDENTIFY DEVICE
pSCIP->cBufferSize = 0;
pSCOP->cBufferSize = sizeof(IDINFO); //
bResult = ::DeviceIoControl(hDevice, // 设备句柄
DFP_RECEIVE_DRIVE_DATA, // 指定IOCTL
pSCIP, sizeof(SENDCMDINPARAMS) - 1, // 输入数据缓冲区
pSCOP, sizeof(SENDCMDOUTPARAMS) + sizeof(IDINFO) - 1, // 输出数据缓冲区
&dwOutBytes, // 输出数据长度
(LPOVERLAPPED)NULL); // 用同步I/O // 复制设备参数结构
::memcpy(pIdInfo, pSCOP->bBuffer, sizeof(IDINFO)); // 释放输入/输出数据空间
::GlobalFree(pSCOP);
::GlobalFree(pSCIP);
return bResult;
}
但是,我发现无论是机械硬盘还是固态硬盘word217都是0,也就是无返回
数据的结构体为IDENTIFY_DEVICE_DATA,该结构体本身很长,有兴趣的可以去查查看。
调查工作到这里就陷入僵局了,但是我发现在上文中,其中提过可以通过TRIM属性来判断是否是SSD属性。但是有可能出现偏差。因为早期的SSD和组成RAID的SSD没有TRIM属性。但是觉得总比什么都没有强吧,我就尝试着获取TRIM属性。获取属性其实和上面获取硬件信息是一样的都是通过函数DeviceIoControl.。只不过输入输出的结构体和IOCTL指令不同罢了,输入的结构体为STORAGE_PROPERTY_QUERY,输出的结构体为DEVICE_TRIM_DESCRIPTOR,IOCTL的指令为IOCTL_STORAGE_QUERY_PROPERTY。完成以后测试,在不同类型的固态和机械硬盘上测试后发现都可以正常识别。看来偏差情况还是很少发生的。至此调查结束。
测试结果: