实战 DeviceIoControl 系列 之二:获取软盘/硬盘/光盘的参数
Q 在 MSDN的那个 demo 中,将设备名换成“A:”取 A 盘参数,先用资源管理
器读一下盘,再运行这个程序可以成功,但换一张盘后就失败;换成“CDROM0”
取 CDROM参数,无论如何都不行。这个问题如何解决呢?
A 取软盘参数是从软盘上读取格式化后的信息,也就是必须执行读操作,这一
点与硬盘不同。将CreateFile中的访问方式改为 GENERIC_READ 就行了。
IOCTL_DISK_GET_DRIVE_GEOMETRY这个 I/O 控制码,对软盘和硬盘有效,但
对一些可移动媒介如 CD/DVD-ROM、TAPE等就不管用了。要取CDROM参数,
还得另辟蹊径。IOCTL_STORAGE_GET_MEDIA_TYPES_EX能够帮我们解决问
题。
Q 使用这些 I/O 控制码,需要什么样的输入输出数据格式呢?
A DeviceIoControl使用这两个控制码时,都不需要输入数据。
IOCTL_DISK_GET_DRIVE_GEOMETRY直接输出一个 DISK_GEOMETRY结构:
typedef struct _DISK_GEOMETRY {
其中 CD-ROM属于“可移动盘”的范围。请注意,GET_MEDIA_TYPES结构本身只定义了一条 DEVICE_MEDIA_INFO,额外的DEVICE_MEDIA_INFO 需要紧接此结构的另外的空间。
LARGE_INTEGER Cylinders; // 柱面数
MEDIA_TYPE MediaType; // 介质类型
DWORD TracksPerCylinder; // 每柱面的磁道数
DWORD SectorsPerTrack; // 每磁道的扇区数
DWORD BytesPerSector; // 每扇区的字节数
} DISK_GEOMETRY;
IOCTL_STORAGE_GET_MEDIA_TYPES_EX 输出一个 GET_MEDIA_TYPES结构:
typedef struct _GET_MEDIA_TYPES {
DWORD DeviceType; // 设备类型
DWORD MediaInfoCount; // 介质信息条数
DEVICE_MEDIA_INFO MediaInfo[1]; // 介质信息
} GET_MEDIA_TYPES;
让我们来看一下 DEVICE_MEDIA_INFO 结构的定义:
typedef struct _DEVICE_MEDIA_INFO {
union {
struct {
LARGE_INTEGER Cylinders; // 柱面数
STORAGE_MEDIA_TYPE MediaType; // 介质类型
DWORD TracksPerCylinder; // 每柱面的磁道数
DWORD SectorsPerTrack; // 每磁道的扇区数
DWORD BytesPerSector; // 每扇区的字节数
DWORD NumberMediaSides; // 介质面数
DWORD MediaCharacteristics; // 介质特性
} DiskInfo; // 硬盘信息 struct {
LARGE_INTEGER Cylinders; // 柱面数
STORAGE_MEDIA_TYPE MediaType; // 介质类型
DWORD TracksPerCylinder; // 每柱面的磁道数
DWORD SectorsPerTrack; // 每磁道的扇区数
DWORD BytesPerSector; // 每扇区的字节数
DWORD NumberMediaSides; // 介质面数
DWORD MediaCharacteristics; // 介质特性
} RemovableDiskInfo; // “可移动盘”信息
struct {
STORAGE_MEDIA_TYPE MediaType; // 介质类型
DWORD MediaCharacteristics; // 介质特性
DWORD CurrentBlockSize; // 块的大小
} TapeInfo; // 磁带信息
} DeviceSpecific;
} DEVICE_MEDIA_INFO;
Q 调用方法我了解了,请用 VC举个例子来实现我所期待已久的功能吧?
A 好,现在就演示一下如何取软盘/硬盘/光盘的参数。测试时,记得要有软盘/
光盘插在驱动器里喔!
首先,用 MFC AppWizard 生成一个单文档的应用程序,取名为DiskGeometry,让它的 View基于 CEditView。
然后,添加以下的.h 和.cpp 文件。
/////////////////////////////////////////////////////////////////////
/////////
// GetDiskGeometry.h /////////////////////////////////////////////////////////////////////
/////////
#if !defined(GET_DISK_GEOMETRY_H__)
#define GET_DISK_GEOMETRY_H__
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <winioctl.h>
BOOL GetDriveGeometry(const char* filename, DISK_GEOMETRY *pdg);
#endif // !defined(GET_DISK_GEOMETRY_H__)
/////////////////////////////////////////////////////////////////////
/////////
// GetDiskGeometry.cpp
/////////////////////////////////////////////////////////////////////
/////////
#include "stdafx.h"
#include "GetDiskGeometry.h"
// IOCTL_STORAGE_GET_MEDIA_TYPES_EX可能返回不止一条DEVICE_MEDIA_INFO,
故定义足够的空间
#define MEDIA_INFO_SIZE
sizeof(GET_MEDIA_TYPES) 15*sizeof(DEVICE_MEDIA_INFO)
// filename -- 用于设备的文件名
// pdg -- 参数缓冲区指针
BOOL GetDriveGeometry(const char* filename, DISK_GEOMETRY *pdg)
{
HANDLE hDevice; // 设备句柄
BOOL bResult; // DeviceIoControl 的返回结果
GET_MEDIA_TYPES *pmt; // 内部用的输出缓冲区
DWORD dwOutBytes; // 输出数据长度
// 打开设备
hDevice = ::CreateFile(filename, // 文件名
GENERIC_READ, // 软驱需要读盘
FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享方式
NULL, // 默认的安全描述符
OPEN_EXISTING, // 创建方式
0, // 不需设置文件属性
NULL); // 不需参照模板文件
if (hDevice == INVALID_HANDLE_VALUE)
{
// 设备无法打开...
return FALSE;
}
// 用 IOCTL_DISK_GET_DRIVE_GEOMETRY 取磁盘参数
bResult = ::DeviceIoControl(hDevice, // 设备句柄
IOCTL_DISK_GET_DRIVE_GEOMETRY, // 取磁盘参数
NULL, 0, // 不需要输入数据 pdg, sizeof(DISK_GEOMETRY), // 输出数据缓冲区
&dwOutBytes, // 输出数据长度
(LPOVERLAPPED)NULL); // 用同步 I/O
// 如果失败,再用 IOCTL_STORAGE_GET_MEDIA_TYPES_EX 取介质类型参数
if (!bResult)
{
pmt = (GET_MEDIA_TYPES *)new BYTE[MEDIA_INFO_SIZE];
bResult = ::DeviceIoControl(hDevice, // 设备句柄
IOCTL_STORAGE_GET_MEDIA_TYPES_EX, // 取介质类型参数
NULL, 0, // 不需要输入数据
pmt, MEDIA_INFO_SIZE, // 输出数据缓冲区
&dwOutBytes, // 输出数据长度
(LPOVERLAPPED)NULL); // 用同步 I/O
if (bResult)
{
// 注意到结构 DEVICE_MEDIA_INFO 是在结构 DISK_GEOMETRY 的基础上扩充的
// 为简化程序,用 memcpy 代替如下多条赋值语句:
// pdg->MediaType =
(MEDIA_TYPE)pmt->MediaInfo[0].DeviceSpecific.DiskInfo.MediaType;
// pdg->Cylinders =
pmt->MediaInfo[0].DeviceSpecific.DiskInfo.Cylinders;
// pdg->TracksPerCylinder =
pmt->MediaInfo[0].DeviceSpecific.DiskInfo.TracksPerCylinder;
// ... ...
::memcpy(pdg, pmt->MediaInfo, sizeof(DISK_GEOMETRY)); }
delete pmt;
}
// 关闭设备句柄
::CloseHandle(hDevice);
return (bResult);
}
然后,在 Toolbar的 IDR_MAINFRAME上添加一个按钮,ID 为ID_GET_DISK_GEOMETRY。打开ClassWizard,在 DiskGeometryView 中添加 ID_GET_DISK_GEOMETRY的映射函数OnGetDiskGeometry。打开
DiskGeometryView.cpp,包含头文件 GetDiskGeometry.h。
在 OnGetDiskGeometry 中,添加以下代码
const char *szDevName[]=
{
".A:",
".B:",
".PhysicalDrive0",
".PhysicalDrive1",
".PhysicalDrive2",
".PhysicalDrive3",
".Cdrom0",
".Cdrom1",
};
DISK_GEOMETRY dg;
ULONGLONG DiskSize;
BOOL bResult; CString strMsg;
CString strTmp;
for (int i = 0; i < sizeof(szDevName)/sizeof(char*); i )
{
bResult = GetDriveGeometry(szDevName[i], &dg);
strTmp.Format("rn%s result = %srn", szDevName[i], bResult ?
"success" : "failure");
strMsg =strTmp;
if (!bResult) continue;
strTmp.Format(" Media Type = %drn", dg.MediaType);
strMsg =strTmp;
strTmp.Format(" Cylinders = %I64drn", dg.Cylinders);
strMsg =strTmp;
strTmp.Format(" Tracks per cylinder = %ldrn", (ULONG)
dg.TracksPerCylinder);
strMsg =strTmp;
strTmp.Format(" Sectors per track = %ldrn", (ULONG)
dg.SectorsPerTrack);
strMsg =strTmp;
strTmp.Format(" Bytes per sector = %ldrn", (ULONG)
dg.BytesPerSector); strMsg =strTmp;
DiskSize = dg.Cylinders.QuadPart * (ULONG)dg.TracksPerCylinder
*
(ULONG)dg.SectorsPerTrack * (ULONG)dg.BytesPerSector;
strTmp.Format(" Disk size = %I64d (Bytes) = %I64d (Mb)rn",
DiskSize, DiskSize / (1024 * 1024));
strMsg =strTmp;
}
CEdit& Edit = GetEditCtrl();
Edit.SetWindowText(strMsg);
最后,最后干什么呢?编译,运行......