收藏 分销(赏)

DeviceIoControl 使用教程.docx

上传人:s4****5z 文档编号:9435744 上传时间:2025-03-26 格式:DOCX 页数:46 大小:67.71KB 下载积分:10 金币
下载 相关 举报
DeviceIoControl 使用教程.docx_第1页
第1页 / 共46页
DeviceIoControl 使用教程.docx_第2页
第2页 / 共46页


点击查看更多>>
资源描述
实战DeviceIoControl 之一:通过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 <windows.h> #include <winioctl.h> 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 = %I64d/n", pdg.Cylinders); printf("Tracks per cylinder = %ld/n", (ULONG) pdg.TracksPerCylinder); printf("Sectors per track = %ld/n", (ULONG) pdg.SectorsPerTrack); printf("Bytes per sector = %ld/n", (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); } 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控制码,需要什么样的输入输出数据格式呢? 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 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; 其中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 <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("/r/n%s result = %s/r/n", szDevName[i], bResult ? "success" : "failure"); strMsg+=strTmp; if (!bResult) continue; strTmp.Format(" Media Type = %d/r/n", dg.MediaType); strMsg+=strTmp; strTmp.Format(" Cylinders = %I64d/r/n", dg.Cylinders); strMsg+=strTmp; strTmp.Format(" Tracks per cylinder = %ld/r/n", (ULONG) dg.TracksPerCylinder); strMsg+=strTmp; strTmp.Format(" Sectors per track = %ld/r/n", (ULONG) dg.SectorsPerTrack); strMsg+=strTmp; strTmp.Format(" Bytes per sector = %ld/r/n", (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)/r/n", DiskSize, DiskSize / (1024 * 1024)); strMsg+=strTmp; } CEdit& Edit = GetEditCtrl(); Edit.SetWindowText(strMsg); 最后,最后干什么呢?编译,运行...... 实战DeviceIoControl 之三:制作磁盘镜像文件 Q DOS命令DISKCOPY给我很深的印象,现在也有许多“克隆”软件,可以对磁盘进行全盘复制。我想,要制作磁盘镜像文件,DeviceIoControl应该很有用武之地吧? A 是的。这里举一个制作软盘镜像文件,功能类似于“DISKCOPY”的例子。 本例实现其功能的核心代码如下: // 打开磁盘 HANDLE OpenDisk(LPCTSTR filename) { HANDLE hDisk; // 打开设备 hDisk = ::CreateFile(filename, // 文件名 GENERIC_READ | GENERIC_WRITE, // 读写方式 FILE_SHARE_READ | FILE_SHARE_WRITE, // 共享方式 NULL, // 默认的安全描述符 OPEN_EXISTING, // 创建方式 0, // 不需设置文件属性 NULL); // 不需参照模板文件 return hDisk; } // 获取磁盘参数 BOOL GetDiskGeometry(HANDLE hDisk, PDISK_GEOMETRY lpGeometry) { DWORD dwOutBytes; BOOL bResult; // 用IOCTL_DISK_GET_DRIVE_GEOMETRY取磁盘参数 bResult = ::DeviceIoControl(hDisk, // 设备句柄 IOCTL_DISK_GET_DRIVE_GEOMETRY, // 取磁盘参数 NULL, 0, // 不需要输入数据 lpGeometry, sizeof(DISK_GEOMETRY), // 输出数据缓冲区 &dwOutBytes, // 输出数据长度 (LPOVERLAPPED)NULL); // 用同步I/O return bResult; } // 从指定磁道开始读磁盘 BOOL ReadTracks(HANDLE hDisk, PDISK_GEOMETRY lpGeometry, LPVOID pBuf, DWORD dwStartCylinder, DWORD dwCylinderNumber) { DWORD VirtBufSize; DWORD BytesRead; // 大小 VirtBufSize = lpGeometry->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 = dwStartCy
展开阅读全文

开通  VIP会员、SVIP会员  优惠大
下载10份以上建议开通VIP会员
下载20份以上建议开通SVIP会员


开通VIP      成为共赢上传

当前位置:首页 > 教育专区 > 其他

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2025 宁波自信网络信息技术有限公司  版权所有

客服电话:4009-655-100  投诉/维权电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服