实战 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,                 // 默认的安全描述符
     PEN_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);

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用DeviceIOControl函数获取物理磁盘的个数。具体实现方法如下: 1. 打开一个物理磁盘设备句柄,调用CreateFile函数,设置参数为: 文件名:物理磁盘设备路径,例如"\\\\.\\PhysicalDrive0",数字0表示第一个物理磁盘。 访问模式:GENERIC_READ | GENERIC_WRITE 共享模式:FILE_SHARE_READ | FILE_SHARE_WRITE 安全性描述符:NULL 2. 调用DeviceIOControl函数,设置参数为: 设备句柄:上一步中返回的句柄 控制码:IOCTL_STORAGE_QUERY_PROPERTY 输入缓冲区:STORAGE_PROPERTY_QUERY结构体,其中的PropertyId字段设置为StorageDeviceProperty,QueryType字段设置为PropertyStandardQuery 输入缓冲区大小:sizeof(STORAGE_PROPERTY_QUERY) 输出缓冲区:存放设备属性信息的缓冲区,可以先设置为一定大小,如果返回的信息超过缓冲区大小,则继续调用DeviceIOControl函数,并增大缓冲区大小,直到返回的信息能够全部存放在缓冲区中 输出缓冲区大小:缓冲区大小 返回值:如果函数调用成功,则返回非零值,否则返回0 3. 解析返回的设备属性信息,判断是否为物理磁盘,并统计物理磁盘的个数。 4. 关闭设备句柄,调用CloseHandle函数。 参考代码如下: ```c++ #include <windows.h> #include <winioctl.h> #include <stdio.h> int main() { HANDLE hDevice = CreateFile("\\\\.\\PhysicalDrive0", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hDevice == INVALID_HANDLE_VALUE) { printf("CreateFile failed, error code: %d\n", GetLastError()); return 0; } STORAGE_PROPERTY_QUERY query; query.PropertyId = StorageDeviceProperty; query.QueryType = PropertyStandardQuery; STORAGE_DESCRIPTOR_HEADER header; DWORD dwBytesReturned = 0; BOOL bRet = DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), &header, sizeof(header), &dwBytesReturned, NULL); if (!bRet) { printf("DeviceIoControl failed, error code: %d\n", GetLastError()); CloseHandle(hDevice); return 0; } DWORD dwBufferSize = sizeof(STORAGE_DEVICE_DESCRIPTOR) + 512; PSTORAGE_DEVICE_DESCRIPTOR pDesc = (PSTORAGE_DEVICE_DESCRIPTOR)malloc(dwBufferSize); ZeroMemory(pDesc, dwBufferSize); pDesc->Size = dwBufferSize; bRet = DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), pDesc, dwBufferSize, &dwBytesReturned, NULL); if (!bRet) { printf("DeviceIoControl failed, error code: %d\n", GetLastError()); CloseHandle(hDevice); free(pDesc); return 0; } if ((pDesc->DeviceType == DIRECT_ACCESS_DEVICE) && (pDesc->BusType == BusTypeSata)) { printf("This is a physical disk.\n"); } else { printf("This is not a physical disk.\n"); } CloseHandle(hDevice); free(pDesc); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值