windows的磁盘操作之七——获取当前所有的物理磁盘号

2011-07-28 17:47:56

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://cutebunny.blog.51cto.com/301216/625577
有了前几节的基础后,本节给出一个更复杂但却非常实用的例子。
很多情况下,我们想知道当前系统下安装了多少块磁盘,他们的物理驱动器号都是多少,每一块磁盘上有多少个分区,分区号怎么分布,每个分区大小是多少。这就类似于我们打开windows的磁盘管理看到的那种非常清晰的列表。对于后几个问题,我们根据物理驱动器号调用第五节http://cutebunny.blog.51cto.com/301216/624567中的GetPartitionLetterFromPhysicalDrive函数,以及第三节http://cutebunny.blog.51cto.com/301216/624079中的GetDiskDriveLayout函数即可搞定。那么我们这一节的重点放在如何获得当前所有物理驱动器号上。
先引入一个新的概念,设备GUID,它是同类设备统一并且唯一的标识码。对于磁盘,GUID为GUID_DEVINTERFACE_DISK,具体值为{53F56307-B6BF-11D0-94F2-00A0C91EFB8B}。windows提供一组API,可以通过GUID枚举出所有该类型的设备。先给出几个相关API的简要介绍
 
HDEVINFO
SetupDiGetClassDevs(
IN LPGUID  ClassGuid,  OPTIONAL
IN PCTSTR  Enumerator,  OPTIONAL
IN HWND  hwndParent,  OPTIONAL
IN DWORD  Flags
);
其中,ClassGuid填入我们感兴趣的设备GUID,该函数返回满足查询条件的一组设备的信息集合的句柄,该句柄就是获取设备信息的关键钥匙。
 
WINSETUPAPI BOOL WINAPI
SetupDiEnumDeviceInterfaces(
IN HDEVINFO  DeviceInfoSet,
IN PSP_DEVINFO_DATA  DeviceInfoData,  OPTIONAL
IN LPGUID  InterfaceClassGuid,
IN DWORD  MemberIndex,
OUT PSP_DEVICE_INTERFACE_DATA  DeviceInterfaceData
);
该函数枚举SetupDiGetClassDevs获得的句柄中包含的所有设备。参数DeviceInfoSet填入我们上一步中获得的句柄,InterfaceClassGuid仍旧是我们感兴趣的GUID,MemberIndex为设备在集合中的索引,从0开始计数,最后DeviceInterfaceData是输出参数,存储枚举出的设备接口,后续可通过此接口获得详细的设备信息。
注意,参数DeviceInterfaceData.cbSize在调用前必须初始化为sizeof(SP_DEVICE_INTERFACE_DATA),这是函数的强制要求。
 
WINSETUPAPI BOOL WINAPI
SetupDiGetDeviceInterfaceDetail(
IN HDEVINFO  DeviceInfoSet,
IN PSP_DEVICE_INTERFACE_DATA  DeviceInterfaceData,
OUT PSP_DEVICE_INTERFACE_DETAIL_DATA  DeviceInterfaceDetailData,  OPTIONAL
IN DWORD  DeviceInterfaceDetailDataSize,
OUT PDWORD  RequiredSize,  OPTIONAL
OUT PSP_DEVINFO_DATA  DeviceInfoData  OPTIONAL
);
该函数根据上两步中的句柄和接口获取设备的详细信息数据。参数DeviceInfoSet和DeviceInterfaceData在上两步中获得。输出参数DeviceInterfaceDetailData存储着设备信息数据,这个结构体中的成员DevicePath就是我们辛辛苦苦找寻的东西了。用它可以作为设备名调用CreateFile函数打开设备,之后的操作,嘿嘿,你懂的…
 
下面是具体代码
/******************************************************************************
* Function: get device path from GUID
* input: lpGuid, GUID pointer
* output: pszDevicePath, device paths
* return: Succeed, the amount of found device paths
*         Fail, -1
******************************************************************************/
DWORD GetDevicePath(LPGUID lpGuid, CHAR **pszDevicePath)
{
    HDEVINFO hDevInfoSet;
    SP_DEVICE_INTERFACE_DATA ifdata;
    PSP_DEVICE_INTERFACE_DETAIL_DATA pDetail;
    DWORD nCount;
    BOOL result;
 
    //get a handle to a device information set
    hDevInfoSet = SetupDiGetClassDevs(
                    lpGuid,      // class GUID
                    NULL,        // Enumerator
                    NULL,        // hwndParent
                    DIGCF_PRESENT | DIGCF_DEVICEINTERFACE    // present devices
                    );
 
    //fail...
    if (hDevInfoSet == INVALID_HANDLE_VALUE)
    {
        fprintf(stderr, "IOCTL_STORAGE_GET_DEVICE_NUMBER Error: %ld\n", GetLastError());
        return (DWORD)-1;
    }
 
    pDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)malloc(INTERFACE_DETAIL_SIZE);
    if (pDetail == NULL)
    {
        return (DWORD)-1;
    }
    pDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
 
    nCount = 0;
    result = TRUE;
 
    // device index = 0, 1, 2... test the device interface one by one
    while (result)
    {
        ifdata.cbSize = sizeof(ifdata);
 
        //enumerates the device interfaces that are contained in a device information set
        result = SetupDiEnumDeviceInterfaces(
                    hDevInfoSet,     // DeviceInfoSet
                    NULL,            // DeviceInfoData
                    lpGuid,          // GUID
                    nCount,   // MemberIndex
                    &ifdata        // DeviceInterfaceData
                    );
        if (result)
        {
            // get details about a device interface
            result = SetupDiGetDeviceInterfaceDetail(
                        hDevInfoSet,    // DeviceInfoSet
                        &ifdata,        // DeviceInterfaceData
                        pDetail,        // DeviceInterfaceDetailData
                        INTERFACE_DETAIL_SIZE,    // DeviceInterfaceDetailDataSize
                        NULL,           // RequiredSize
                        NULL          // DeviceInfoData
                        );
            if (result)
            {
                // copy the path to output buffer
                strcpy(pszDevicePath[nCount], pDetail->DevicePath);
                //printf("%s\n", pDetail->DevicePath);
                nCount++;
            }
        }
    }
 
    free(pDetail);
    (void)SetupDiDestroyDeviceInfoList(hDevInfoSet);
 
    return nCount;
}
执行完毕后,所有满足条件的磁盘设备名称都存储在字符串数组pszDevicePath中。有了这个关键的数组,后面就可以为所欲为了。
 
以下是获得所有物理磁盘号的完整代码
/******************************************************************************
* Function: get all present disks' physical number
* input: N/A
* output: ppDisks, array of disks' physical number
* return: Succeed, the amount of present disks
*         Fail, -1
******************************************************************************/
DWORD GetAllPresentDisks(DWORD **ppDisks)
{
    CHAR *szDevicePath[MAX_DEVICE];        // device path
    DWORD nDevice;
    HANDLE hDevice;
    STORAGE_DEVICE_NUMBER number;
    BOOL result;
    DWORD readed;
    WORD i, j;
 
    for (i = 0; i < MAX_DEVICE; i++)
    {
        szDevicePath[i] = (CHAR *)malloc(INTERFACE_DETAIL_SIZE);
        if (NULL == szDevicePath[i])
        {
            for (j = 0; j < i; j++)
            {
                free(szDevicePath[i]);
            }
            return (DWORD)-1;
        }
    }
 
    // get the device paths
    nDevice = GetDevicePath(const_cast<LPGUID>(&GUID_DEVINTERFACE_DISK), szDevicePath);
    if ((DWORD)-1 == nDevice)
    {
        for (i = 0; i < MAX_DEVICE; i++)
        {
            free(szDevicePath[i]);
        }
        return (DWORD)-1;
    }
 
    *ppDisks = (DWORD *)malloc(sizeof(DWORD) * nDevice);
    // get the disk's physical number one by one
    for (i = 0; i < nDevice; i++)
    {
        hDevice = CreateFile(
                    szDevicePath[i], // drive to open
                    GENERIC_READ | GENERIC_WRITE,     // access to the drive
                    FILE_SHARE_READ | FILE_SHARE_WRITE, //share mode
                    NULL,             // default security attributes
                    OPEN_EXISTING,    // disposition
                    0,                // file attributes
                    NULL            // do not copy file attribute
                    );
        if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive
        {
            for (j = 0; j < MAX_DEVICE; j++)
            {
                free(szDevicePath[j]);
            }
            free(*ppDisks);
            fprintf(stderr, "CreateFile() Error: %ld\n", GetLastError());
            return DWORD(-1);
        }
        result = DeviceIoControl(
                    hDevice,                // handle to device
                    IOCTL_STORAGE_GET_DEVICE_NUMBER, // dwIoControlCode
                    NULL,                            // lpInBuffer
                    0,                               // nInBufferSize
                    &number,           // output buffer
                    sizeof(number),         // size of output buffer
                    &readed,       // number of bytes returned
                    NULL      // OVERLAPPED structure
                    );
        if (!result) // fail
        {
            fprintf(stderr, "IOCTL_STORAGE_GET_DEVICE_NUMBER Error: %ld\n", GetLastError());
            for (j = 0; j < MAX_DEVICE; j++)
            {
                free(szDevicePath[j]);
            }
            free(*ppDisks);
            (void)CloseHandle(hDevice);
            return (DWORD)-1;
        }
        *(*ppDisks + i) = number.DeviceNumber;
 
        (void)CloseHandle(hDevice);
    }
 
    for (i = 0; i < MAX_DEVICE; i++)
    {
        free(szDevicePath[i]);
    }
    return nDevice;
}
代码说明:
1. 调用函数GetDevicePath获得前面所说的磁盘设备名称数组。
2. 对每一个磁盘设备,调用CreateFile打开并获得设备句柄。
3. 调用操作码为IOCTL_STORAGE_GET_DEVICE_NUMBER的DeviceIoControl函数获得磁盘物理驱动器号。
4. 将所有物理磁盘号存入数组返回。
 
大功告成了。可能有朋友会问,GetDevicePath不是已经获得了磁盘路径么,你前面说过,这个路径不是\\.\PhysicalDriveX就是\\.\X: ,那我们解析一下这个字符串不就可以获得磁盘号或者盘符了么。很可惜,这里的磁盘路径出现了第三种形式,而且是毫无章法的形式。打开函数GetDevicePath中的注释行//printf("%s\n",
pDetail->DevicePath);将这种形式的路径打印出来,可以看到类似为
\\?\ide#diskwdc_wd1600aajs-08b4a0___________________01.03a01#5&245a6b6d&0&0.0.0#
{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
\\?\ide#diskwdc_wd1600aajs-08b4a0___________________01.03a01#5&37141c12&0&0.1.0#
{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
所以,没办法,我们还是得用DeviceIoControl找出磁盘号。

本文出自 “bunny技术坊” 博客,请务必保留此出处http://cutebunny.blog.51cto.com/301216/625577

BTnode备注:运行不正常请在头部加上

#include "Setupapi.h"//add pzqu 20160807 for GetPhysicalDiskUse:function

#include "Ntddstor.h"//add pzqu 20160807 for GetPhysicalDiskUse:function

#define INTERFACE_DETAIL_SIZE    (1024)//add pzqu 20160807 for GetPhysicalDiskUse:function

#define MAX_DEVICE                 8//add pzqu 20160807 for GetPhysicalDiskUse:function

#pragma comment(lib, "Setupapi.lib")//add pzqu 20160807 for GetPhysicalDiskUse:function

windows的磁盘操作之七——获取当前所有的物理磁盘号 加备注的更多相关文章

  1. Linux修复磁盘操作

    Linux修复磁盘操作 1.卸载分区 umount /data 修复磁盘,先判读是磁盘坏了,还是文件系统坏了:一般可以修复,但是建议换磁盘. 2.fsck  -y /dev/sda1  ##-y 参数 ...

  2. 虚拟机中如何挂载物理磁盘(VMware操作)

    测试的时候难免会遇到,从真是机器拷贝东西到虚拟机中,虽说安装了VMware tools(Vm→Install VMware tools...),就可以将文件直接拖到虚拟机里面去,但是这样拷贝总是需要花 ...

  3. windows下C语言编程获取磁盘(分区)使用情况

    windows下编程获取磁盘(分区)使用情况 windows下编程获取磁盘(分区)使用情况 GetLogicalDriveStrings函数 使用示例 获取需要的缓冲区长度示例 获取所有驱动器号示例 ...

  4. windows的磁盘操作之九——区分本地磁盘与移动硬盘

    http://cutebunny.blog.51cto.com/301216/674443 最近碰到了个新问题,记录下来作为windows的磁盘操作那个系列的续篇吧. 一些时候我们的程序需要区分本地存 ...

  5. windows的磁盘操作之四——根据逻辑分区号获得物理磁盘号(转)

    第一节中我们谈到了磁盘设备名称的两种形式: 对于物理驱动器x,形式为\\.\PhysicalDriveX,编号从0开始,例如 名称 含义 \\.\PhysicalDrive0 打开第一个物理驱动器 \ ...

  6. python linux 磁盘操作

    #coding:utf-8 ''' __author__ = 'similarface' connection:841196883@qq.com 磁盘操作 ''' import psutil impo ...

  7. windows phone 使用相机并获取图片(3)

    原文:windows phone 使用相机并获取图片(3) 使用相机需要引用如下命名空间 " Margin="12,10,12,0" ></Image> ...

  8. 浅谈.NET(C#)与Windows用户账户信息的获取

    原文:浅谈.NET(C#)与Windows用户账户信息的获取 目录 1. 用户账户名称 - 使用Environment类 2. 用户账户信息 - 使用WindowsIdentity和IdentityR ...

  9. Windows Azure Virtual Machine (38) 跨租户迁移使用托管磁盘的Azure虚拟机

    <Windows Azure Platform 系列文章目录> 背景介绍: (1)我们建议使用Azure Manage Disk托管磁盘来创建Azure虚拟机 (2)使用托管磁盘的好处是, ...

随机推荐

  1. 使用jqueryUI实现自由调整表格列宽

    今天项目中需要插入表格,用Excel表格调整列宽时,想怎么拖就怎么拖,于是乎就让插入的表格也这么让人舒服.网上查找许久,没找到好用的方案.最后发现jQuery UI中的resizable()方法可以实 ...

  2. 个人永久性免费-Excel催化剂功能第18波-在Excel上也能玩上词云图

    这年头数据可视化日新月异,在Excel上做数据分析,最后一步,难免要搞个图表输出高大上一回,微软也深知此道,在Excel2016上更新了一大波图表功能,市场上很耀眼的词云图还是没加进来,虽然在各大的在 ...

  3. 一个简单的JS倒计时

    看到很多商城都是抢购倒计时的功能,今天闲来无事做了个倒计时.全当学习JS. 主要思路:主要用到Date对象,声明一个变量获取当前时间,在声明一个变量获取结束时间,结束时间-当前时间=剩余时间(倒计时) ...

  4. 面向对象和pickle模块结合

    面向对象和pickle模块相关 1.面向对象: class 类名: def init(self,参数1,参数2): self.对象的属性1 = 参数1 self.对象的属性2 = 参数2 def 方法 ...

  5. 前端响应式痛点解决之box-sizing

    前置 在 CSS 盒子模型的默认定义里,你对一个元素所设置的 width 与 height 只会应用到这个元素的内容区.如果这个元素有任何的 border 或 padding ,绘制到屏幕上时的盒子宽 ...

  6. python初步编写用户登录

     python初步编写用户登录 python编写用户登录 用python写一个脚本,使得这个脚本在执行后,可以产生如下的效果: 1.用户的账号为:root 密码为:westos 2.用户账号和密码均输 ...

  7. 2019前端面试系列——Vue面试题

    Vue 双向绑定原理        mvvm 双向绑定,采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty()来劫持各个属性的 setter.getter,在数 ...

  8. 1.Java概述、安装及环境搭建

    1. 前言 1.1 学习方法 1. 学会学习的方法 2. 学会独立解决问题 3. 主动的学习而不是被动的接受 4. 知识的大家的,能力才是自己的 1.2 推荐博客 当代程序员都应该养成写博客.看博客的 ...

  9. 关于写自定义的SQL接口出现的问题

    1.<if test="   as != ' ' "></if> 与    <if test='   as != " "    ' ...

  10. Android通过辅助功能实现抢微信红包原理简单介绍

    简书文章:https://www.jianshu.com/p/e1099a94b979 附抢红包开源项目地址,代码已全改为Kotlin了,已适配到最新微信7.0.5版本,如果对你有所帮助赏个star吧 ...