实战 DeviceIoControl 系列 之二:获取软盘/硬盘/光盘的参数

实战 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);
最后,最后干什么呢?编译,运行...... 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值