本文是根据Lynn McGuire的著名程序 DiskID32程序基础修改而来的 ,部分关键代码,仅供参考.
主要用到了ATA/APAPI的IDENTIFY DEVICE指令,
ATA/APAPI是T13起草和发布的IDE/EIDE/UDMA硬盘及其它可移动存储设备与主机接口的标准,
该接口标准规定了ATA/ATAPI设备的输入输出寄存器和指令集.
用到的常量和结构如下
#include <winioctl.h>
#define IOCTL_STORAGE_QUERY_PROPERTY CTL_CODE(IOCTL_STORAGE_BASE, 0x0500, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define DFP_RECEIVE_DRIVE_DATA CTL_CODE(IOCTL_DISK_BASE, 0x0022, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IDE_ATA_IDENTIFY 0xEC // ATA的ID指令(IDENTIFY DEVICE)
#define FILE_DEVICE_SCSI 0x0000001b
#define IOCTL_SCSI_MINIPORT_IDENTIFY ((FILE_DEVICE_SCSI << 16) + 0x0501)
#define IOCTL_SCSI_MINIPORT 0x0004D008 // see NTDDSCSI.H for definition
IDENTIFY DEVICE专门用于固定硬盘,而IDENTIFY PACKET DEVICE用于可移动存储设备如CDROM、CF、MO、ZIP、TAPE等。因为驱动程序的原因,不管是IDENTIFY DEVICE也好,IDENTIFY PACKET DEVICE也好,获取可移动存储设备的详细信息,一般是做不到的。而且除了IDE硬盘,对SCSI、USB等接口的硬盘也不起作用。除非厂商提供的驱动支持这样的功能。另外ATA/ATAPI有很多指令,如READ SECTORS, WRITE SECTORS, SECURITY, SLEEP, STANDBY等,对这些指令操作的过程中也要很慎重.
最新的SDK已经声明了很多以前需要手工声明的结构类型,如

结构声明:
typedef enum _STORAGE_QUERY_TYPE {
PropertyStandardQuery = 0 , // 读取描述
PropertyExistsQuery, // 测试是否支持
PropertyMaskQuery, // 读取指定的描述
PropertyQueryMaxDefined // 验证数据
} STORAGE_QUERY_TYPE, * PSTORAGE_QUERY_TYPE;
// 查询存储设备还是适配器属性
typedef enum _STORAGE_PROPERTY_ID {
StorageDeviceProperty = 0 , // 查询设备属性
StorageAdapterProperty // 查询适配器属性
} STORAGE_PROPERTY_ID, * PSTORAGE_PROPERTY_ID;
// 查询属性输入的数据结构
typedef struct _STORAGE_PROPERTY_QUERY {
STORAGE_PROPERTY_ID PropertyId; // 设备/适配器
STORAGE_QUERY_TYPE QueryType; // 查询类型
UCHAR AdditionalParameters[ 1 ]; // 额外的数据(仅定义了象征性的1个字节)
} STORAGE_PROPERTY_QUERY, * PSTORAGE_PROPERTY_QUERY;
typedef struct _STORAGE_DEVICE_DESCRIPTOR {
ULONG Version; // 版本
ULONG Size; // 结构大小
UCHAR DeviceType; // 设备类型
UCHAR DeviceTypeModifier; // SCSI-2额外的设备类型
BOOLEAN RemovableMedia; // 是否可移动
BOOLEAN CommandQueueing; // 是否支持命令队列
ULONG VendorIdOffset; // 厂家设定值的偏移
ULONG ProductIdOffset; // 产品ID的偏移
ULONG ProductRevisionOffset; // 产品版本的偏移
ULONG SerialNumberOffset; // 序列号的偏移
STORAGE_BUS_TYPE BusType; // 总线类型
ULONG RawPropertiesLength; // 额外的属性数据长度
UCHAR RawDeviceProperties[ 1 ]; // 额外的属性数据(仅定义了象征性的1个字节)
} STORAGE_DEVICE_DESCRIPTOR, * PSTORAGE_DEVICE_DESCRIPTOR;
{
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;
头文件声明的几个重要函数
BOOL IdentifyDevice(HANDLE hDevice, PIDINFO pIdInfo);
BOOL GetPhysicalDriveInfoInNT( int nDrive, PIDINFO pIdInfo);
void GetBoisUUID();
CString MakeActualString(LPCTSTR pszMsg);
BOOL GetDriveProperty(HANDLE hDevice, PSTORAGE_DEVICE_DESCRIPTOR pDevDesc);
BOOL GetDriveGeometry(HANDLE hDevice, PDISK_GEOMETRY pGeometry);
HANDLE OpenDevice(LPCTSTR pszDevicePath);
int GetDevicePath(LPGUID lpGuid, LPTSTR * pszDevicePath);
cpp
#define BUFSIZE1 1024
#define INTERFACE_DETAIL_SIZE (1024)
#define MEDIA_INFO_SIZE (sizeof(GET_MEDIA_TYPES) + sizeof(DEVICE_MEDIA_INFO) * 15)
#define MAX_DEVICE 16
const GUID DiskClassGuid = { 0x53f56307L , 0xb6bf , 0x11d0 , { 0x94 , 0xf2 , 0x00 , 0xa0 , 0xc9 , 0x1e , 0xfb , 0x8b }};
{
HANDLE hDevice;
PDISK_GEOMETRY pGeometry;
PSTORAGE_DEVICE_DESCRIPTOR pDevDesc;
int i,j;
int nDevice;
char * szDevicePath[MAX_DEVICE];
pGeometry = new DISK_GEOMETRY;
CString strTmp = "" ;
for (i = 0 ;i < MAX_DEVICE;i ++ ) szDevicePath[i] = new char [ 256 ];
pDevDesc = (PSTORAGE_DEVICE_DESCRIPTOR) new BYTE[ sizeof (STORAGE_DEVICE_DESCRIPTOR) + 512 - 1 ];
pDevDesc -> Size = sizeof (STORAGE_DEVICE_DESCRIPTOR) + 512 - 1 ;
LPGUID lpGuid = (LPGUID) & DiskClassGuid;
for (i = 0 ; i < sizeof (lpGuid) / sizeof (LPGUID); i ++ )
{
nDevice = GetDevicePath(lpGuid, szDevicePath);
for (j = 0 ; j < nDevice; j ++ )
{
hDevice = OpenDevice( szDevicePath[j] );
if (hDevice != INVALID_HANDLE_VALUE)
{
GetDriveGeometry(hDevice, pGeometry);
GetDriveProperty(hDevice, pDevDesc);
char * p;
p = ( char * )pDevDesc;
strTmp = (pDevDesc -> ProductRevisionOffset ? & p[pDevDesc -> ProductRevisionOffset] : "" ); // firmware version
strTmp = MakeActualString(strTmp);
strTmp = (pDevDesc -> SerialNumberOffset ? & p[pDevDesc -> SerialNumberOffset] : "" );
strTmp = MakeActualString(strTmp);
strTmp = (pDevDesc -> ProductIdOffset ? & p[pDevDesc -> ProductIdOffset] : "" );
strTmp = MakeActualString(strTmp);
ULONGLONG DiskSize = pGeometry -> Cylinders.QuadPart * (ULONGLONG)pGeometry -> TracksPerCylinder *
(ULONGLONG)pGeometry -> SectorsPerTrack * (ULONGLONG)pGeometry -> BytesPerSector;
strTmp.Format( " Disk size = %I64d Bytes (%I64d MB) " , DiskSize, DiskSize / ( 1024 * 1024 ));
CloseHandle(hDevice);
}
}
}
delete pGeometry;
for (i = 0 ;i < MAX_DEVICE;i ++ ) delete []szDevicePath[i];
}
{
CString strInfo;
IDINFO IdInfo;
for ( int i = 0 ; i < 4 ; i ++ ) // 一般不会挂十来块硬盘吧
{
if (GetPhysicalDriveInfoInNT(i, & IdInfo))
{
strInfo += " " + MakeIdeInfo(i, 0 , & IdInfo);
}
}
}
{
HDEVINFO hDevInfoSet;
SP_DEVICE_INTERFACE_DATA ifdata;
PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail;
int nCount;
BOOL bResult;
hDevInfoSet = ::SetupDiGetClassDevs(lpGuid,
NULL,
NULL,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if ( INVALID_HANDLE_VALUE == hDevInfoSet )
{
return 0 ;
}
pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)::GlobalAlloc(LMEM_ZEROINIT, INTERFACE_DETAIL_SIZE);
pDetail -> cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA);
nCount = 0 ;
bResult = TRUE;
while (bResult)
{
ifdata.cbSize = sizeof (ifdata);
// 枚举符合该GUID的设备接口
bResult = ::SetupDiEnumDeviceInterfaces(
hDevInfoSet, // 设备信息集句柄
NULL, // 不需额外的设备描述
lpGuid, // GUID
(ULONG)nCount, // 设备信息集里的设备序号
& ifdata); // 设备接口信息
if (bResult)
{
// 取得该设备接口的细节(设备路径)
bResult = SetupDiGetInterfaceDeviceDetail(
hDevInfoSet, // 设备信息集句柄
& ifdata, // 设备接口信息
pDetail, // 设备接口细节(设备路径)
INTERFACE_DETAIL_SIZE, // 输出缓冲区大小
NULL, // 不需计算输出缓冲区大小(直接用设定值)
NULL); // 不需额外的设备描述
if (bResult)
{
// 复制设备路径到输出缓冲区
::strcpy(pszDevicePath[nCount], pDetail -> DevicePath);
// 调整计数值
nCount ++ ;
}
}
}
// 释放设备接口数据空间
::GlobalFree(pDetail);
// 关闭设备信息集句柄
::SetupDiDestroyDeviceInfoList(hDevInfoSet);
return nCount;
}
HANDLE CCGuardTestDlg::OpenDevice(LPCTSTR pszDevicePath)
{
HANDLE hDevice;
// 打开设备
hDevice = ::CreateFile(pszDevicePath, // 设备路径
GENERIC_READ | GENERIC_WRITE, // 读写方式
FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享方式
NULL, // 默认的安全描述符
OPEN_EXISTING, // 创建方式
0 , // 不需设置文件属性
NULL); // 不需参照模板文件
return hDevice;
}
BOOL CCGuardTestDlg::GetDriveGeometry(HANDLE hDevice, PDISK_GEOMETRY pGeometry)
{
PGET_MEDIA_TYPES pMediaTypes; // 内部用的输出缓冲区
DWORD dwOutBytes; // 输出数据长度
BOOL bResult; // DeviceIoControl的返回结果
// 申请内部用的输出缓冲区
pMediaTypes = (PGET_MEDIA_TYPES)::GlobalAlloc(LMEM_ZEROINIT, MEDIA_INFO_SIZE);
bResult = ::DeviceIoControl(hDevice, // 设备句柄
IOCTL_STORAGE_GET_MEDIA_TYPES_EX, // 取介质类型参数
NULL, 0 , // 不需要输入数据
pMediaTypes, MEDIA_INFO_SIZE, // 输出数据缓冲区
& dwOutBytes, // 输出数据长度
(LPOVERLAPPED)NULL); // 用同步I/O
if (bResult)
{
// 注意到结构DEVICE_MEDIA_INFO是在结构DISK_GEOMETRY的基础上扩充的
// 为简化程序,用memcpy代替如下多条赋值语句:
// pGeometry->MediaType = (MEDIA_TYPE)pMediaTypes->MediaInfo[0].DeviceSpecific.DiskInfo.MediaType;
// pGeometry->Cylinders = pMediaTypes->MediaInfo[0].DeviceSpecific.DiskInfo.Cylinders;
// pGeometry->TracksPerCylinder = pMediaTypes->MediaInfo[0].DeviceSpecific.DiskInfo.TracksPerCylinder;
// ... ...
::memcpy(pGeometry, pMediaTypes -> MediaInfo, sizeof (DISK_GEOMETRY));
}
::GlobalFree(pMediaTypes);
return bResult;
}
BOOL CCGuardTestDlg::GetDriveProperty(HANDLE hDevice, PSTORAGE_DEVICE_DESCRIPTOR pDevDesc)
{
STORAGE_PROPERTY_QUERY Query; // 查询输入参数
DWORD dwOutBytes; // IOCTL输出数据长度
BOOL bResult; // IOCTL返回值
// 指定查询方式
Query.PropertyId = StorageDeviceProperty;
Query.QueryType = PropertyStandardQuery;
// 用IOCTL_STORAGE_QUERY_PROPERTY取设备属性信息
bResult = ::DeviceIoControl(hDevice, // 设备句柄
IOCTL_STORAGE_QUERY_PROPERTY, // 取设备属性信息
& Query, sizeof (STORAGE_PROPERTY_QUERY), // 输入数据缓冲区
pDevDesc, pDevDesc -> Size, // 输出数据缓冲区
& dwOutBytes, // 输出数据长度
(LPOVERLAPPED)NULL); // 用同步I/O
return bResult;
}
CString CCGuardTestDlg::MakeActualString(LPCTSTR pszMsg)
{
int i;
int len;
BOOL fNeedTranslation;
CString strMsg;
len = ::strlen(pszMsg);
fNeedTranslation = TRUE;
i = 0 ;
while (i < len && fNeedTranslation)
{
if ( ! (pszMsg[i] >= ' 0 ' && pszMsg[i] <= ' 9 ' ||
pszMsg[i] >= ' A ' && pszMsg[i] <= ' F ' ||
pszMsg[i] >= ' a ' && pszMsg[i] <= ' f ' ))
{
fNeedTranslation = FALSE;
}
i ++ ;
}
if (fNeedTranslation)
{
WORD word;
for (i = 0 ; i < len; i += 4 )
{
::sscanf( & pszMsg[i], " %04x " , & word);
strMsg += ( char )(word % 256 );
strMsg += ( char )(word / 256 );
}
}
else
{
strMsg = pszMsg;
}
strMsg.TrimLeft();
strMsg.TrimRight();
return strMsg;
}
BOOL CCGuardTestDlg::GetPhysicalDriveInfoInNT( int nDrive, PIDINFO pIdInfo)
{
HANDLE hDevice; // 设备句柄
BOOL bResult; // 返回结果
char szFileName[ 20 ]; // 文件名
::sprintf(szFileName, " //./PhysicalDrive%d " , nDrive);
// nDevice = GetDevicePath(lpGuid, szDevicePath);
hDevice = OpenDevice(szFileName);
if (hDevice == INVALID_HANDLE_VALUE)
{
return FALSE;
}
// IDENTIFY DEVICE
bResult = IdentifyDevice(hDevice, pIdInfo);
if (bResult)
{
// 调整字符串
AdjustString(pIdInfo -> sSerialNumber, 20 );
AdjustString(pIdInfo -> sModelNumber, 40 );
AdjustString(pIdInfo -> sFirmwareRev, 8 );
}
::CloseHandle (hDevice);
return bResult;
}
BOOL CCGuardTestDlg::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 );
pSCIP -> irDriveRegs.bCommandReg = IDE_ATA_IDENTIFY; // really important!
// 指定输入/输出数据缓冲区大小
pSCIP -> cBufferSize = 0 ;
pSCOP -> cBufferSize = sizeof (IDINFO);
// IDENTIFY DEVICE
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;
}
void CCGuardTestDlg::AdjustString(char *str, int len)
{
char ch;
int i;
// 两两颠倒
for(i=0;i<len;i+=2)
{
ch = str[i];
str[i] = str[i+1];
str[i+1] = ch;
}
// 若是右对齐的,调整为左对齐 (去掉左边的空格)
i=0;
while(i<len && str[i]==' ') i++;
::memmove(str, &str[i], len-i);
// 去掉右边的空格
i = len - 1;
while(i>=0 && str[i]==' ')
{
str[i] = '/0';
i--;
}
}