【DeviceIOControl】 二、获取软盘/硬盘/光盘的参数

在上面那个例子中,将设备名换成“A:”取A盘参数,先用资源管理器读一下盘,再运行这个程序可以成功,但换一张盘后就失败;换成“CDROM0”取CDROM参数,无论如何都不行。这个问题如何解决呢?

取软盘参数是从软盘上读取格式化后的信息,也就是必须执行读操作,这一点与硬盘不同。将CreateFile中的访问方式改为GENERIC_READ就行了。IOCTL_DISK_GET_DRIVE_GEOMETRY这个I/O控制码,对软盘和硬盘有效,但对一些可移动媒介如CD/DVD-ROM、TAPE等就不管用了。要取CDROM参数,还得另辟蹊径。IOCTL_STORAGE_GET_MEDIA_TYPES_EX能够帮我们解决问题。

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需要紧接此结构的另外的空间。


现在就演示一下如何取软盘/硬盘/光盘的参数。测试时,记得要有软盘/光盘插在驱动器里喔!

首先,用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);


最后,最后干什么呢?编译,运行......

你可能感兴趣的:(数据结构,mfc)