實戰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);

最後,最後幹什麼呢?編譯,運行......

[相關資源]

本文Demo源碼:DiskGeometry.zip (21KB) bhw98的專欄:http://www.csdn.net/develop/author/netauthor/bhw98/

首次發佈:2003-02-18
最後修訂:2003-05-20

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章