




免费预览已结束,剩余28页可下载查看
下载本文档
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
设备驱动接口函数DeviceIoControl使用实例1. 通过API访问设备驱动程序Q 在NT/2000/XP中,我想用VC编写应用程序访问硬件设备,如获取磁盘参数、读写绝对扇区数据、测试光驱实际速度等,该从哪里入手呢? A 在NT/2000/XP中,应用程序可以通过API函数DeviceIoControl来实现对设备的访问获取信息,发送命令,交换数据等。利用该接口函数向指定的设备驱动发送正确的控制码及数据,然后分析它的响应,就可以达到我们的目的。 DeviceIoControl的函数原型为 BOOL DeviceIoControl( HANDLE hDevice, / 设备句柄 DWORD dwIoControlCode, / 控制码 LPVOID lpInBuffer, / 输入数据缓冲区指针 DWORD nInBufferSize, / 输入数据缓冲区长度 LPVOID lpOutBuffer, / 输出数据缓冲区指针 DWORD nOutBufferSize, / 输出数据缓冲区长度 LPDWORD lpBytesReturned, / 输出数据实际长度单元长度 LPOVERLAPPED lpOverlapped / 重叠操作结构指针);设备句柄用来标识你所访问的设备。 发送不同的控制码,可以调用设备驱动程序的不同类型的功能。在头文件winioctl.h中,预定义的标准设备控制码,都以IOCTL或FSCTL开头。例如,IOCTL_DISK_GET_DRIVE_GEOMETRY是对物理驱动器取结构参数(介质类型、柱面数、每柱面磁道数、每磁道扇区数等)的控制码,FSCTL_LOCK_VOLUME是对逻辑驱动器的卷加锁的控制码。输入输出数据缓冲区是否需要,是何种结构,以及占多少字节空间,完全由不同设备的不同操作类型决定。在头文件winioctl.h中,已经为标准设备预定义了一些输入输出数据结构。重叠操作结构指针设置为NULL,DeviceIoControl将进行阻塞调用;否则,应在编程时按异步操作设计。Q 设备句柄是从哪里获得的?A 设备句柄可以用API函数CreateFile获得。它的原型为 HANDLE CreateFile( LPCTSTR lpFileName, / 文件名/设备路径 DWORD dwDesiredAccess, / 访问方式 DWORD dwShareMode, / 共享方式 LPSECURITY_ATTRIBUTES lpSecurityAttributes, / 安全描述符指针 DWORD dwCreationDisposition, / 创建方式 DWORD dwFlagsAndAttributes, / 文件属性及标志 HANDLE hTemplateFile / 模板文件的句柄);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/ROM CDROMx 磁带机 TAPEx 其中,物理驱动器不包括软驱和光驱。逻辑驱动器可以是IDE/SCSI/PCMCIA/USB接口的硬盘分区(卷)、光驱、MO、CF卡等,甚至是虚拟盘。x=0,1,2 其它的设备名称需通过驱动接口的GUID调用设备管理函数族取得,这里暂不讨论。Q 请举一个简单的例子说明如何通过DeviceIoControl访问设备驱动程序。A 这里有一个从MSDN上摘抄来的demo程序,演示在NT/2000/XP中如何通过DeviceIoControl获取硬盘的基本参数。/* The code of interest is in the subroutine GetDriveGeometry. The code in main shows how to interpret the results of the IOCTL call. */ #include #include BOOL GetDriveGeometry(DISK_GEOMETRY *pdg) HANDLE hDevice; / handle to the drive to be examined BOOL bResult; / results flag DWORD junk; / discard results hDevice = CreateFile(.PhysicalDrive0, / drive to open 0, / no access to the drive FILE_SHARE_READ | / share mode FILE_SHARE_WRITE, NULL, / default security attributes OPEN_EXISTING, / disposition 0, / file attributes NULL); / do not copy file attributes if (hDevice = INVALID_HANDLE_VALUE) / cannot open the drive return (FALSE); bResult = DeviceIoControl(hDevice, / device to be queried IOCTL_DISK_GET_DRIVE_GEOMETRY, / operation to perform NULL, 0, / no input buffer pdg, sizeof(*pdg), / output buffer &junk, / # bytes returned (LPOVERLAPPED) NULL); / synchronous I/O CloseHandle(hDevice); return (bResult); int main(int argc, char *argv) DISK_GEOMETRY pdg; / disk drive geometry structure BOOL bResult; / generic results flag ULONGLONG DiskSize; / size of the drive, in bytes bResult = GetDriveGeometry (&pdg); if (bResult) printf(Cylinders = %I64dn, pdg.Cylinders); printf(Tracks per cylinder = %ldn, (ULONG) pdg.TracksPerCylinder); printf(Sectors per track = %ldn, (ULONG) pdg.SectorsPerTrack); printf(Bytes per sector = %ldn, (ULONG) pdg.BytesPerSector); DiskSize = pdg.Cylinders.QuadPart * (ULONG)pdg.TracksPerCylinder * (ULONG)pdg.SectorsPerTrack * (ULONG)pdg.BytesPerSector; printf(Disk size = %I64d (Bytes) = %I64d (Mb)n, DiskSize, DiskSize / (1024 * 1024); else printf(GetDriveGeometry failed. Error %ld.n, GetLastError(); return (int)bResult);现在我们总结一下通过DeviceIoControl访问设备驱动程序的“三步曲”:首先用CreateFile取得设备句柄,然后用DeviceIoControl与设备进行I/O,最后别忘记用CloseHandle关闭设备句柄。 2. 获取软盘/硬盘/光盘的参数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 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 MediaInfo1; / 介质信息 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;其中CD-ROM属于“可移动盘”的范围。请注意,GET_MEDIA_TYPES结构本身只定义了一条DEVICE_MEDIA_INFO,额外的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 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 BYTEMEDIA_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-MediaInfo0.DeviceSpecific.DiskInfo.MediaType; / pdg-Cylinders = pmt-MediaInfo0.DeviceSpecific.DiskInfo.Cylinders; / pdg-TracksPerCylinder = pmt-MediaInfo0.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 TracksPerCylinder * lpGeometry-SectorsPerTrack * lpGeometry-BytesPerSector; / 偏移 :SetFilePointer(hDisk, VirtBufSize*dwStartCylinder, NULL, FILE_BEGIN); return :ReadFile(hDisk, pBuf, VirtBufSize*dwCylinderNumber, &BytesRead, NULL); / 从指定磁道开始写磁盘BOOL WriteTracks(HANDLE hDisk, PDISK_GEOMETRY lpGeometry, LPVOID pBuf, DWORD dwStartCylinder, DWORD dwCylinderNumber) DWORD VirtBufSize; DWORD BytesWritten; / 大小 VirtBufSize = lpGeometry-TracksPerCylinder * lpGeometry-SectorsPerTrack * lpGeometry-BytesPerSector; / 偏移 :SetFilePointer(hDisk, VirtBufSize*dwStartCylinder, NULL, FILE_BEGIN); return :WriteFile(hDisk, pBuf, VirtBufSize*dwCylinderNumber, &BytesWritten, NULL); / 从指定磁道开始格式化磁盘BOOL LowLevelFormatTracks(HANDLE hDisk, PDISK_GEOMETRY lpGeometry, DWORD dwStartCylinder, DWORD dwCylinderNumber) FORMAT_PARAMETERS FormatParameters; PBAD_TRACK_NUMBER lpBadTrack; DWORD dwOutBytes; DWORD dwBufSize; BOOL bResult; FormatParameters.MediaType = lpGeometry-MediaType; FormatParameters.StartCylinderNumber = dwStartCylinder; FormatParameters.EndCylinderNumber = dwStartCylinder + dwCylinderNumber - 1; FormatParameters.StartHeadNumber = 0; FormatParameters.EndHeadNumber = lpGeometry-TracksPerCylinder - 1; dwBufSize = lpGeometry-TracksPerCylinder * sizeof(BAD_TRACK_NUMBER); lpBadTrack = (PBAD_TRACK_NUMBER) new BYTEdwBufSize; / 用IOCTL_DISK_FORMAT_TRACKS对连续磁道进行低级格式化 bResult = :DeviceIoControl(hDisk, / 设备句柄 IOCTL_DISK_FORMAT_TRACKS, / 低级格式化 &FormatParameters, sizeof(FormatParameters), / 输入数据缓冲区 lpBadTrack, dwBufSize, / 输出数据缓冲区 &dwOutBytes, / 输出数据长度 (LPOVERLAPPED)NULL); / 用同步I/O delete lpBadTrack; return bResult; / 将卷锁定BOOL LockVolume(HANDLE hDisk) DWORD dwOutBytes; BOOL bResult; / 用FSCTL_LOCK_VOLUME锁卷 bResult = :DeviceIoControl(hDisk, / 设备句柄 FSCTL_LOCK_VOLUME, / 锁卷 NULL, 0, / 不需要输入数据 NULL, 0, / 不需要输出数据 &dwOutBytes, / 输出数据长度 (LPOVERLAPPED)NULL); / 用同步I/O return bResult; / 将卷解锁BOOL UnlockVolume(HANDLE hDisk) DWORD dwOutBytes; BOOL bResult; / 用FSCTL_UNLOCK_VOLUME开卷锁 bResult = :DeviceIoControl(hDisk, / 设备句柄 FSCTL_UNLOCK_VOLUME, / 开卷锁 NULL, 0, / 不需要输入数据 NULL, 0, / 不需要输出数据 &dwOutBytes, / 输出数据长度 (LPOVERLAPPED)NULL); / 用同步I/O return bResult; / 将卷卸下/ 该操作使系统重新辨识磁盘,等效于重新插盘BOOL DismountVolume(HANDLE hDisk) DWORD dwOutBytes; BOOL bResult; / 用FSCTL_DISMOUNT_VOLUME卸卷 bResult = :DeviceIoControl(hDisk, / 设备句柄 FSCTL_DISMOUNT_VOLUME, / 卸卷 NULL, 0, / 不需要输入数据 NULL, 0, / 不需要输出数据 &dwOutBytes, / 输出数据长度 (LPOVERLAPPED)NULL); / 用同步I/O return bResult;将软盘保存成镜像文件的步骤简单描述为: 1、创建空的镜像文件。 2、调用OpenDisk打开软盘。成功转3,失败转8。 3、调用LockVolume将卷锁定。成功转4,失败转7。 4、调用GetDiskGeometry获取参数。成功转5,失败转6。 5、将磁盘参数写入镜像文件作为文件头。调用ReadTracks按柱面读出数据,保存在镜像文件中。循环次数等于柱面数。 6、调用UnlockVolume将卷解锁。 7、调用CloseDisk关闭软盘。 8、关闭镜像文件。 将镜像文件载入软盘的步骤简单描述为:1、打开镜像文件。2、调用OpenDisk打开软盘。成功转3,失败转11。3、调用LockVolume将卷锁定。成功转4,失败转10。4、调用GetDiskGeometry获取参数。成功转5,失败转9。5、从镜像文件中读出文件头,判断两个磁盘参数是否一致。不一致转6,否则转7。6、调用LowLevelFormatTracks按柱面格式化软盘。循环次数等于柱面数。成功转7,失败转8。7、从镜像文件中读出数据,并调用WriteTracks按柱面写入磁盘。循环次数等于柱面数。8、调用DismountVolume将卷卸下。9、调用UnlockVolume将卷解锁。10、调用CloseDisk关闭软盘。11、关闭镜像文件。Q 我注意到,磁盘读写和格式化是按柱面进行的,有什么道理吗? A 没有特别的原因,只是因为在这个例子中可以方便地显示处理进度。 有一点需要特别提及,按绝对地址读写磁盘数据时,“最小单位”是扇区,地址一定要与扇区对齐,长度也要等于扇区长度的整数倍。比如,每扇区512字节,那么起始地址和数据长度都应能被512整除才行。 Q 我忽然产生了一个伟大的想法,用绝对地址读写的方式使用磁盘,包括U盘啦,MO啦,而不是用现成的文件系统,那不是可以将数据保密了吗? A 当然,只要你喜欢。可千万别在你的系统盘上做试验,否则.可别怪bhw98没有提醒过你喔! Q 我知道怎么测试光驱的传输速度了,就用上面的方法,读出一定长度数据,除以所需时间,应该可以吧? A 可以。但取光盘参数时要用IOCTL_STORAGE_GET_MEDIA_TYPES_EX,我们已经探讨过的4. 获取硬盘的详细信息Q 用IOCTL_DISK_GET_DRIVE_GEOMETRYIOCTL_STORAGE_GET_MEDIA_TYPES_EX只能得到很少的磁盘参数,我想获得包括硬盘序列号在内的更加详细的信息,有什么办法呀?A 确实,用你所说的I/O控制码,只能得到最基本的磁盘参数。获取磁盘出厂信息的I/O控制码,微软在VC/MFC环境中没有开放,在DDK中可以发现一些线索。早先,Lynn McGuire写了一个很出名的获取IDE硬盘详细信息的程序DiskID32,下面的例子是在其基础上经过增删和改进而成的。本例中,我们要用到ATA/APAPI的IDENTIFY DEVICE指令。ATA/APAPI是国际组织T13起草和发布的IDE/EIDE/UDMA硬盘及其它可移动存储设备与主机接口的标准,至今已经到了ATA/APAPI-7版本。该接口标准规定了ATA/ATAPI 设备的输入输出寄存器和指令集。欲了解更详细的ATA/ATAPI技术资料,可访问T13的站点。用到的常量及数据结构有以下一些:/ IOCTL控制码/ #define DFP_SEND_DRIVE_COMMAND0x0007c084#define DFP_SEND_DRIVE_COMMANDCTL_CODE(IOCTL_DISK_BASE, 0x0021,METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)/ #define DFP_RECEIVE_DRIVE_DATA0x0007c088#define DFP_RECEIVE_DRIVE_DATA CTL_CODE(IOCTL_DISK_BASE, 0x0022,METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)#define FILE_DEVICE_SCSI0x0000001b#define IOCTL_SCSI_MINIPORT_IDENTIFY (FILE_DEVICE_SCSI 16) + 0x0501)#define IOCTL_SCSI_MINIPORT 0x0004D008/ see NTDDSCSI.H for definition/ ATA/ATAPI指令#define IDE_ATA_IDENTIFY 0xEC/ ATA的ID指令(IDENTIFY DEVICE)/ IDE命令寄存器typede
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 六一活动系列周活动方案
- 六一活动造型活动方案
- 六一游园比赛活动方案
- 六一班级气球活动方案
- 六一绘画促销活动方案
- 六一节活动茶楼活动方案
- 六一蛋糕店集赞活动方案
- 六年级猜灯谜活动方案
- 医技报名考试试题及答案
- 业余团校考试试题及答案
- 四川省内江市市中区2025年小数毕业模拟试卷(含答案)
- 《中国传统节庆文化》课件
- 公路养护基础知识
- 3-6岁儿童学习与发展指南-语言
- 养老护理员知识培训课件
- 2025-2030中国袋式除尘器市场需求前景与发展动向追踪研究报告
- 学校传染病防控培养课件
- GB/T 19598-2025地理标志产品质量要求安溪铁观音
- 施工现场安全防护标准化图集
- 城区建筑垃圾处理资源再利用设备采购 投标方案(技术方案)
- 《国际商事调解》课件
评论
0/150
提交评论