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

来源:互联网 发布:阿里云安装yum 编辑:程序博客网 时间:2024/06/06 04:43

 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("rn%s result = %srn", szDevName[i], bResult ?
"success" : "failure");
    strMsg+=strTmp;
 
    if (!bResult) continue;
 
    strTmp.Format("  Media Type = %drn", dg.MediaType);
    strMsg+=strTmp;
 
    strTmp.Format("  Cylinders = %I64drn", dg.Cylinders);
    strMsg+=strTmp;
 
    strTmp.Format("  Tracks per cylinder = %ldrn", (ULONG)
dg.TracksPerCylinder);
    strMsg+=strTmp;
 
    strTmp.Format("  Sectors per track = %ldrn", (ULONG)
dg.SectorsPerTrack);
    strMsg+=strTmp;
 
    strTmp.Format("  Bytes per sector = %ldrn", (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)rn",
DiskSize, DiskSize / (1024 * 1024));
    strMsg+=strTmp;
  }
 
  CEdit& Edit = GetEditCtrl();
 
  Edit.SetWindowText(strMsg);

 

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

原创粉丝点击