实战DeviceIoControl.docx
《实战DeviceIoControl.docx》由会员分享,可在线阅读,更多相关《实战DeviceIoControl.docx(67页珍藏版)》请在冰豆网上搜索。
![实战DeviceIoControl.docx](https://file1.bdocx.com/fileroot1/2023-1/26/ae368912-03c5-4941-bed4-46b0f4879485/ae368912-03c5-4941-bed4-46b0f48794851.gif)
实战DeviceIoControl
实战DeviceIoControl之一:
通过API访问设备驱动程序
Q在NT/2000/XP中,我想用VC编写应用程序访问硬件设备,如获取磁盘参数、读写绝对扇区数据、测试光驱实际速度等,该从哪里入手呢?
A在NT/2000/XP中,应用程序可以通过API函数DeviceIoControl来实现对设备的访问—获取信息,发送命令,交换数据等。
利用该接口函数向指定的设备驱动发送正确的控制码及数据,然后分析它的响应,就可以达到我们的目的。
DeviceIoControl的函数原型为
BOOLDeviceIoControl(
HANDLEhDevice, //设备句柄
DWORDdwIoControlCode, //控制码
LPVOIDlpInBuffer, //输入数据缓冲区指针
DWORDnInBufferSize, //输入数据缓冲区长度
LPVOIDlpOutBuffer, //输出数据缓冲区指针
DWORDnOutBufferSize, //输出数据缓冲区长度
LPDWORDlpBytesReturned, //输出数据实际长度单元长度
LPOVERLAPPEDlpOverlapped //重叠操作结构指针
);
设备句柄用来标识你所访问的设备。
发送不同的控制码,可以调用设备驱动程序的不同类型的功能。
在头文件winioctl.h中,预定义的标准设备控制码,都以IOCTL或FSCTL开头。
例如,IOCTL_DISK_GET_DRIVE_GEOMETRY是对物理驱动器取结构参数(介质类型、柱面数、每柱面磁道数、每磁道扇区数等)的控制码,FSCTL_LOCK_VOLUME是对逻辑驱动器的卷加锁的控制码。
输入输出数据缓冲区是否需要,是何种结构,以及占多少字节空间,完全由不同设备的不同操作类型决定。
在头文件winioctl.h中,已经为标准设备预定义了一些输入输出数据结构。
重叠操作结构指针设置为NULL,DeviceIoControl将进行阻塞调用;否则,应在编程时按异步操作设计。
Q设备句柄是从哪里获得的?
A设备句柄可以用API函数CreateFile获得。
它的原型为
HANDLECreateFile(
LPCTSTRlpFileName, //文件名/设备路径
DWORDdwDesiredAccess, //访问方式
DWORDdwShareMode, //共享方式
LPSECURITY_ATTRIBUTESlpSecurityAttributes,//安全描述符指针
DWORDdwCreationDisposition, //创建方式
DWORDdwFlagsAndAttributes, //文件属性及标志
HANDLEhTemplateFile //模板文件的句柄
);
CreateFile这个函数用处很多,这里我们用它“打开”设备驱动程序,得到设备的句柄。
操作完成后用CloseHandle关闭设备句柄。
与普通文件名有所不同,设备驱动的“文件名”(常称为“设备路径”)形式固定为“\.DeviceName”(注意在C程序中该字符串写法为“\\.\DeviceName”),DeviceName必须与设备驱动程序内定义的设备名称一致。
一般地,调用CreateFile获得设备句柄时,访问方式参数设置为0或GENERIC_READ|GENERIC_WRITE,共享方式参数设置为FILE_SHARE_READ|FILE_SHARE_WRITE,创建方式参数设置为OPEN_EXISTING,其它参数设置为0或NULL。
Q可是,我怎么知道设备名称是什么呢?
A一些存储设备的名称是微软定义好的,不可能有什么变化。
大体列出如下软盘驱动器A:
B:
硬盘逻辑分区C:
D:
E:
...
物理驱动器PHYSICALDRIVEx
CD-ROM,DVD/ROMCDROMx
磁带机TAPEx
其中,物理驱动器不包括软驱和光驱。
逻辑驱动器可以是IDE/SCSI/PCMCIA/USB接口的硬盘分区(卷)、光驱、MO、CF卡等,甚至是虚拟盘。
x=0,1,2……
其它的设备名称需通过驱动接口的GUID调用设备管理函数族取得,这里暂不讨论。
Q请举一个简单的例子说明如何通过DeviceIoControl访问设备驱动程序。
A这里有一个从MSDN上摘抄来的demo程序,演示在NT/2000/XP中如何通过DeviceIoControl获取硬盘的基本参数。
/*ThecodeofinterestisinthesubroutineGetDriveGeometry.The
codeinmainshowshowtointerprettheresultsoftheIOCTLcall.*/
#include
#include
BOOLGetDriveGeometry(DISK_GEOMETRY*pdg)
{
HANDLEhDevice; //handletothedrivetobeexamined
BOOLbResult; //resultsflag
DWORDjunk; //discardresults
hDevice=CreateFile("\\.\PhysicalDrive0", //drivetoopen
0, //noaccesstothedrive
FILE_SHARE_READ|//sharemode
FILE_SHARE_WRITE,
NULL, //defaultsecurityattributes
OPEN_EXISTING, //disposition
0, //fileattributes
NULL); //donotcopyfileattributes
if(hDevice==INVALID_HANDLE_VALUE)//cannotopenthedrive
{
return(FALSE);
}
bResult=DeviceIoControl(hDevice, //devicetobequeried
IOCTL_DISK_GET_DRIVE_GEOMETRY, //operationtoperform
NULL,0, //noinputbuffer
pdg,sizeof(*pdg), //outputbuffer
&junk, //#bytesreturned
(LPOVERLAPPED)NULL); //synchronousI/O
CloseHandle(hDevice);
return(bResult);
}
intmain(intargc,char*argv[])
{
DISK_GEOMETRYpdg; //diskdrivegeometrystructure
BOOLbResult; //genericresultsflag
ULONGLONGDiskSize; //sizeofthedrive,inbytes
bResult=GetDriveGeometry(&pdg);
if(bResult)
{
printf("Cylinders=%I64dn",pdg.Cylinders);
printf("Trackspercylinder=%ldn",(ULONG)pdg.TracksPerCylinder);
printf("Sectorspertrack=%ldn",(ULONG)pdg.SectorsPerTrack);
printf("Bytespersector=%ldn",(ULONG)pdg.BytesPerSector);
DiskSize=*(ULONG)pdg.TracksPerCylinder*
(ULONG)pdg.SectorsPerTrack*(ULONG)pdg.BytesPerSector;
printf("Disksize=%I64d(Bytes)=%I64d(Mb)n",DiskSize,
DiskSize/(1024*1024));
}
else
{
printf("GetDriveGeometryfailed.Error%ld.n",GetLastError());
}
return((int)bResult);
}
Q如果将设备名换成“A:
”就可以取A盘参数,换成“CDROM0”就可以取CDROM参数,是这样吗?
A这个问题暂不做回答。
请动手试一下。
现在我们总结一下通过DeviceIoControl访问设备驱动程序的“三步曲”:
首先用CreateFile取得设备句柄,然后用DeviceIoControl与设备进行I/O,最后别忘记用CloseHandle关闭设备句柄。
实战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控制码,需要什么样的输入输出数据格式呢?
ADeviceIoControl使用这两个控制码时,都不需要输入数据。
IOCTL_DISK_GET_DRIVE_GEOMETRY直接输出一个DISK_GEOMETRY结构:
typedefstruct_DISK_GEOMETRY{
LARGE_INTEGERCylinders;//柱面数
MEDIA_TYPEMediaType;//介质类型
DWORDTracksPerCylinder;//每柱面的磁道数
DWORDSectorsPerTrack;//每磁道的扇区数
DWORDBytesPerSector;//每扇区的字节数
}DISK_GEOMETRY;
IOCTL_STORAGE_GET_MEDIA_TYPES_EX输出一个GET_MEDIA_TYPES结构:
typedefstruct_GET_MEDIA_TYPES{
DWORDDeviceType;//设备类型
DWORDMediaInfoCount;//介质信息条数
DEVICE_MEDIA_INFOMediaInfo[1];//介质信息
}GET_MEDIA_TYPES;
让我们来看一下DEVICE_MEDIA_INFO结构的定义:
typedefstruct_DEVICE_MEDIA_INFO{
union{
struct{
LARGE_INTEGERCylinders;//柱面数
STORAGE_MEDIA_TYPEMediaType;//介质类型
DWORDTracksPerCylinder;//每柱面的磁道数
DWORDSectorsPerTrack;//每磁道的扇区数
DWORDBytesPerSector;//每扇区的字节数
DWORDNumberMediaSides;//介质面数
DWORDMediaCharacteristics;//介质特性
}DiskInfo;//硬盘信息
struct{
LARGE_INTEGERCylinders;//柱面数
STORAGE_MEDIA_TYPEMediaType;//介质类型
DWORDTracksPerCylinder;//每柱面的磁道数
DWORDSectorsPerTrack;//每磁道的扇区数
DWORDBytesPerSector;//每扇区的字节数
DWORDNumberMediaSides;//介质面数
DWORDMediaCharacteristics;//介质特性
}RemovableDiskInfo;//“可移动盘”信息
struct{
STORAGE_MEDIA_TYPEMediaType;//介质类型
DWORDMediaCharacteristics;//介质特性
DWORDCurrentBlockSize;//块的大小
}TapeInfo;//磁带信息
}DeviceSpecific;
}DEVICE_MEDIA_INFO;
其中CD-ROM属于“可移动盘”的范围。
请注意,GET_MEDIA_TYPES结构本身只定义了一条DEVICE_MEDIA_INFO,额外的DEVICE_MEDIA_INFO需要紧接此结构的另外的空间。
Q调用方法我了解了,请用VC举个例子来实现我所期待已久的功能吧?
A好,现在就演示一下如何取软盘/硬盘/光盘的参数。
测试时,记得要有软盘/光盘插在驱动器里喔!
首先,用MFCAppWizard生成一个单文档的应用程序,取名为DiskGeometry,让它的View基于CEditView。
然后,添加以下的.h和.cpp文件。
//////////////////////////////////////////////////////////////////////////////
//GetDiskGeometry.h
//////////////////////////////////////////////////////////////////////////////
#if!
defined(GET_DISK_GEOMETRY_H__)
#defineGET_DISK_GEOMETRY_H__
#if_MSC_VER>1000
#pragmaonce
#endif//_MSC_VER>1000
#include
BOOLGetDriveGeometry(constchar*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,故定义足够的空间
#defineMEDIA_INFO_SIZEsizeof(GET_MEDIA_TYPES)+15*sizeof(DEVICE_MEDIA_INFO)
//filename--用于设备的文件名
//pdg--参数缓冲区指针
BOOLGetDriveGeometry(constchar*filename,DISK_GEOMETRY*pdg)
{
HANDLEhDevice;//设备句柄
BOOLbResult;//DeviceIoControl的返回结果
GET_MEDIA_TYPES*pmt;//内部用的输出缓冲区
DWORDdwOutBytes;//输出数据长度
//打开设备
hDevice=:
:
CreateFile(filename,//文件名
GENERIC_READ,//软驱需要读盘
FILE_SHARE_READ|FILE_SHARE_WRITE,//共享方式
NULL,//默认的安全描述符
OPEN_EXISTING,//创建方式
0,//不需设置文件属性
NULL);//不需参照模板文件
if(hDevice==INVALID_HANDLE_VALUE)
{
//设备无法打开...
returnFALSE;
}
//用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*)newBYTE[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].;
//pdg->Cylinders=pmt->MediaInfo[0].;
//pdg->TracksPerCylinder=pmt->MediaInfo[0].;
//......
:
:
memcpy(pdg,pmt->MediaInfo,sizeof(DISK_GEOMETRY));
}
deletepmt;
}
//关闭设备句柄
:
:
CloseHandle(hDevice);
return(bResult);
}
然后,在Toolbar的IDR_MAINFRAME上添加一个按钮,ID为ID_GET_DISK_GEOMETRY。
打开ClassWizard,在DiskGeometryView中
添加ID_GET_DISK_GEOMETRY的映射函数OnGetDiskGeometry。
打开DiskGeometryView.cpp,包含头文件GetDiskGeometry.h。
在OnGetDiskGeometry中,添加以下代码constchar*szDevName[]=
{
"\\\\.\\A:
",
"\\\\.\\B:
",
"\\\\.\\PhysicalDrive0",
"\\\\.\\PhysicalDrive1",
"\\\\.\\PhysicalDrive2",
"\\\\.\\PhysicalDrive3",
"\\