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

2023-11-07

转载自:windows的磁盘操作之九——区分本地磁盘与移动硬盘_bunny技术坊的技术博客_51CTO博客

原文如下:

最近碰到了个新问题,记录下来作为windows的磁盘操作那个系列的续篇吧。

一些时候我们的程序需要区分本地存储设备和USB存储设备。在网上搜一搜一般会找到一个最直接的API,GetDriveType,其原型为
UINT GetDriveType(LPCTSTR lpRootPathName)
参数lpRootPathName是存储设备的根目录,例如C:\,返回值即为设备类型。

Return code
Description
DRIVE_REMOVABLE
The drive has removable media; for example, a floppy drive, thumb drive, or flash card reader.
DRIVE_FIXED
The drive has fixed media; for example, a hard drive or flash drive.

或者采用一种稍微复杂一点的方法,使用我们第一节 http://cutebunny.blog.51cto.com/301216/624027中介绍的GetDriveGeometry()函数,其输出参数DISK_GEOMETRY *pdg中的MediaType字段代表设备类型。
typedef enum _MEDIA_TYPE
{
RemovableMedia
FixedMedia
}MEDIA_TYPE;
 
这两个方法看似能方便快捷的解决我们的需求,但事实上当你使用GetDriveType()去获取一块移动硬盘的类型时,程序会坑爹的告诉你这块移动硬盘的类型是DRIVE_FIXED,根本无法与本地磁盘区分开来。GetDriveGeometry()函数的结果也是如此。
事实上,上述方法只对小容量的U盘有效,会返回给你DRIVE_REMOVABLE的结果;而对移动硬盘甚至是一块稍大容量的U盘(比如我有一块格式化为FAT32格式的4G U盘),就无能为力了。
 
所以,我们必须采用别的思路了,这里我介绍一种通过查看总线类型来区分本地磁盘和USB磁盘的方法。当然,其基础还是我们那万能的DeviceIoControl,不过这次的控制码为IOCTL_STORAGE_QUERY_PROPERTY。同时对应的输入参数为STORAGE_PROPERTY_QUERY结构,输出参数为STORAGE_DEVICE_DESCRIPTOR结构体。
typedef struct _STORAGE_PROPERTY_QUERY {
  STORAGE_PROPERTY_ID  PropertyId;
  STORAGE_QUERY_TYPE  QueryType;
  UCHAR  AdditionalParameters[1];
} STORAGE_PROPERTY_QUERY, *PSTORAGE_PROPERTY_QUERY;
调用时需设置输入参数中的字段
PropertyId = StorageDeviceProperty;
QueryType = PropertyStandardQuery;
以表明我们要查询一个device descriptor,也就是说,只有指定这种类型,输出参数才会得到STORAGE_DEVICE_DESCRIPTOR类型数据。
typedef struct _STORAGE_DEVICE_DESCRIPTOR {
  ULONG  Version;
  ULONG  Size;
  UCHAR  DeviceType;
  UCHAR  DeviceTypeModifier;
  BOOLEAN  RemovableMedia;
  BOOLEAN  CommandQueueing;
  ULONG  VendorIdOffset;
  ULONG  ProductIdOffset;
  ULONG  ProductRevisionOffset;
  ULONG  SerialNumberOffset;
  STORAGE_BUS_TYPE  BusType;
  ULONG  RawPropertiesLength;
  UCHAR  RawDeviceProperties[1];
} STORAGE_DEVICE_DESCRIPTOR, *PSTORAGE_DEVICE_DESCRIPTOR;
其中,BusType定义为
typedef enum _STORAGE_BUS_TYPE {
  BusTypeUnknown = 0x00,
  BusTypeScsi,
  BusTypeAtapi,
  BusTypeAta,
  BusType1394,
  BusTypeSsa,
  BusTypeFibre,
  BusTypeUsb,
  BusTypeRAID,
  BusTypeiScsi,
  BusTypeSas,
  BusTypeSata,
  BusTypeSd,
  BusTypeMmc,
  BusTypeMax,
  BusTypeMaxReserved = 0x7F
} STORAGE_BUS_TYPE, *PSTORAGE_BUS_TYPE;
明白了吧,如果总线类型为BusTypeUsb,就是找到了我们的USB移动硬盘了。
但此时还需要解决一个问题,STORAGE_DEVICE_DESCRIPTOR可以理解为一个变长缓冲区,最后一个字段RawDeviceProperties[1]是可以动态扩展的(windows API经常有这种情况),那么函数DeviceIoControl()中的参数nOutBufferSize应该填多少呢?这时我们需要借助另一个数据结构STORAGE_DESCRIPTOR_HEADER,在我们不知道device descriptor实际需要多大的缓冲区时,可以先把STORAGE_DESCRIPTOR_HEADER作为输出参数以获得device descriptor的缓冲区大小,其大小被存入header的size字段中。
typedef struct _STORAGE_DESCRIPTOR_HEADER {
  ULONG  Version;
  ULONG  Size;
} STORAGE_DESCRIPTOR_HEADER, *PSTORAGE_DESCRIPTOR_HEADER;
 
以下是具体代码
/******************************************************************************
* Function: get the bus type of an disk
* input: drive name (c:)
* output: bus type
* return: Succeed, 0
*         Fail, -1
******************************************************************************/
DWORD GetDriveTypeByBus(const CHAR *drive, WORD *type)
{
    HANDLE hDevice;               // handle to the drive to be examined
    BOOL result;                 // results flag
    DWORD readed;                   // discard results
 
    STORAGE_DESCRIPTOR_HEADER *pDevDescHeader;
    STORAGE_DEVICE_DESCRIPTOR *pDevDesc;
    DWORD devDescLength;
    STORAGE_PROPERTY_QUERY query;
 
    hDevice = CreateFile(
                    drive, // 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
    {
        fprintf(stderr, "CreateFile() Error: %ld\n", GetLastError());
        return DWORD(-1);
    }
 
    query.PropertyId = StorageDeviceProperty;
    query.QueryType = PropertyStandardQuery;
 
    pDevDescHeader = (STORAGE_DESCRIPTOR_HEADER *)malloc(sizeof(STORAGE_DESCRIPTOR_HEADER));
    if (NULL == pDevDescHeader)
    {
        return (DWORD)-1;
    }
   
    result = DeviceIoControl(
                    hDevice,     // device to be queried
                    IOCTL_STORAGE_QUERY_PROPERTY,     // operation to perform
                    &query,
                    sizeof query,               // no input buffer
                    pDevDescHeader,
                    sizeof(STORAGE_DESCRIPTOR_HEADER),     // output buffer
                    &readed,                 // # bytes returned
                    NULL);      // synchronous I/O
    if (!result)        //fail
    {
        fprintf(stderr, "IOCTL_STORAGE_QUERY_PROPERTY Error: %ld\n", GetLastError());
        free(pDevDescHeader);
        (void)CloseHandle(hDevice);
        return DWORD(-1);
    }
 
    devDescLength = pDevDescHeader->Size;
    pDevDesc = (STORAGE_DEVICE_DESCRIPTOR *)malloc(devDescLength);
    if (NULL == pDevDesc)
    {
        free(pDevDescHeader);
        return (DWORD)-1;
    }
 
    result = DeviceIoControl(
                    hDevice,     // device to be queried
                    IOCTL_STORAGE_QUERY_PROPERTY,     // operation to perform
                    &query,
                    sizeof query,               // no input buffer
                    pDevDesc,
                    devDescLength,     // output buffer
                    &readed,                 // # bytes returned
                    NULL);      // synchronous I/O
    if (!result)        //fail
    {
        fprintf(stderr, "IOCTL_STORAGE_QUERY_PROPERTY Error: %ld\n", GetLastError());
        free(pDevDescHeader);
        free(pDevDesc);
        (void)CloseHandle(hDevice);
        return DWORD(-1);
    }
 
    //printf("%d\n", pDevDesc->BusType);
    *type = (WORD)pDevDesc->BusType;
    free(pDevDescHeader);
    free(pDevDesc);
 
    (void)CloseHandle(hDevice);
    return 0;
}
 
代码说明:
1. 调用CreateFile打开并获得设备句柄。
2. 在输入参数STORAGE_PROPERTY_QUERY query中指定查询类型。
3. 以STORAGE_DESCRIPTOR_HEADER *pDevDescHeader为输出参数,调用操作码为IOCTL_STORAGE_QUERY_PROPERTYDeviceIoControl函数获得输出缓冲区大小。
4. 按3中获得的缓冲区大小为STORAGE_DEVICE_DESCRIPTOR *pDevDesc分配空间,以pDevDesc为输出参数,调用操作码为IOCTL_STORAGE_QUERY_PROPERTYDeviceIoControl函数获得device descriptor。
5. 从device descriptor中获得BusType
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

windows的磁盘操作之九——区分本地磁盘与移动硬盘 的相关文章

  • 2个map集合求差集

    List
  • I - Playing With Strings(回文串)

    I Playing With Stringshttps vjudge csgrandeur cn problem Gym 101020I 丹尼和迈克是两个孩子 他们整天玩游戏 当他们找不到游戏玩的时候 他们就发明一个游戏 有一个小时左右到达
  • 用table进行网页的布局

    table width 100 tr td table width 100 tr td img src img logo2 png td td img src img header jpg td td a href 登录 a td tr t
  • var与ES6中const、let声明的变量的区别

    好久以前的研究 今天再来回顾一下 首先我们比较一下使用var声明的变量和不使用var声明的变量的区别 使用var声明的变量声明前调用 那么该变量的值为undefined 不使用var声明的变量声明前调用直接报错Uncaught Refere
  • 如何把项目上传到Gitee(小白必备)

    序言 配置ssh 1 设置 gt ssh公钥 gt 怎么生成公钥 gt 在任意地方打开powerShell运行复制的代码 gt 成功生成的ssh key会展示地址 我这里已经生成了 打开相应的地址 注意 这里复制之后一路enter不需要填信
  • 【Leetcode】1302. 层数最深叶子节点的和

    题目描述 题解 层序遍历是一定要的 而且是分层的层序遍历 也就是在层序遍历的过程中把 结点 val 加起来 但是要的是最后一层 我想不到要怎么判断遍历层最后一层 所以直接把每一层的 结点 val 加起来得到sum 到下一层的时候清空sum
  • EMOTIVE EPOC和EPOC + 快速入门指南

    EMOTIVE EPOC AND EPOC Quick Start Guide EMOTIVE EPOC和EPOC 快速入门指南 1 charge your headset Set your headset to off position
  • 【数据结构】——优先级队列(堆)

    一 概念 在一些情况下 操作的数据可能带有优先级 一般出队列时 可能需要优先级高的元素出队 在这种的情况下 就可以使用优先级队列 返回最高优先级对象和添加新的对象 堆的性质 堆中某个节点的值总是不大于或小于其父亲节点的值 堆总是一颗完全二叉
  • 如何学好人工智能?

    在主流的视频直播教学之外 直接与老师在课件上互动能够更快地让我进入学习状态 大家的时间都很宝贵 当我们好不容易下定决心要学习一门新知识时 各种影响学习的噪声会在本不富裕的耐心上大打折扣 万事开头难变成了真理 特别是在人工智能的专业学习上 不
  • 人脸识别+深度学习,水平远超人类大脑!

    全文共3342字 预计学习时长7分钟 什么是人脸识别 什么是深度学习 两者结合能带来什么影响 如果你认为一篇文章无法涵盖这么多问题 这篇文章能够改变你的想法 本文展示了所有重要的概念 总之 在这篇文章中你将会了解到脸部识别是如何运作的 及其
  • 黑群晖安装与使用

    文章目录 黑群晖安装与使用教程 第一节 准备 1 文件及后缀 2 引导U盘 3 数据硬盘 4 主板及内存 5 电源 6 UPS不间断电源 7 其他配件 一台正常使用的电脑 网络交换机 网线 第二节 制作引导U盘 1 下载引导文件 2 修改引
  • PostgreSQL 字符串 collate 与排序 源码分析

    事情的起因是这样的 某个用户问我为什么在GP里面查询到的 A gt a 和PostgreSQL中查询到的结果不一样 一个是false 一个true 但是这个原因其实和Greenplum还是PostgreSQL是没关系的 原因的根源还是col
  • wordpress + woocommerce 安装主题后页面404问题

    记录一个woocommerce启用主题后的问题 安装的主题是flatsome 启用该主题后除首页以外全部页面都被定向到Nginx404页面 查了一些资料后 首先是伪静态未设置原因 转到服务器宝塔设置页面 gt 网站管理 gt 打开网站设置
  • 调用百度搜索的接口实现简单的百度搜索

  • Unity 各版本下载方法

    开发Unity的 获取不同版本Unity版本和了解Unity最新动态很重要 现在更新迭代很频繁 在开发时 不论遇到项目升级 还是插件要求 还是老项目运行 总是在多个版本间切换 是不是经常遇到 新手们不知道从哪里下载 又或者想要cker版本
  • QMAKE_LFLAGS妙用

    转自 http blog csdn net onlyou930 article details 6423647 当程序依赖的库不断增多时 如果把所有的库放到一起 当目标机上同时也有其他应用程序需要某个特定版本库的时候 这种做法会造成严重的版
  • 竞争与冒险

    竞争与冒险 文章目录 1 竞争与冒险产生原因 2 判断电路是否存在竞争 冒险现象 3 消除竞争与冒险 1 竞争与冒险产生原因 观察以下门电路 Gate1为 非门 Gate2为 与门 实现了逻辑 F A A

随机推荐

  • ant design for vue table表格添加编辑单元格功能

    EditableCell组件页面
  • Java Optional的使用

    Java8我用的最开心肯定是stream 一旦习惯了写法确实代码简洁了不少 当然除了能读懂 还要学会stream的debug用法 否则写的时候很爽 但是一出现问题就特别痛苦 实际上还忽略了一个好的工具 Optional A containe
  • linux内核新版gpio配置

    新版gpio操作
  • 基于python的对比度增强(线性变换、直方图正规化、直方图均衡化、CLAHE)

    线性变换 假设输入图像为I 宽为W 高为H 输出图像为O 图像的线性变换可以用以下公式定义 O r c
  • vscode修改背景

    1 在扩展中输入background 并安装 2 按住 Ctrl 打开设置页面 输入background 在扩展设置中点击 在settings json中编辑 3 在大括号中 输入下列代码来设置背景图的样式 透明度可根据自己的需求修改 代码
  • 计算机网络传输层(上)

    计算机网络传输层 上 传输层概述 1 传输层协议为运行在不同host上的进程提供了一种逻辑通信机制 2 端系统运行传输层协议 发送方 将应用递交的消息分成一个或多个的segment 并向下穿给网络层 接受方 将接受到的segment组装成消
  • Android Gradle plugin requires Java 11 to run. You are currently using Java 1.8

    问题描述 把Gradle插件版本更新到7 0 2之后编译失败 出现如下错误 A problem occurred evaluating project app gt Failed to apply plugin com android in
  • 基于vue2开发的毕业设计(时光云平台)

    自己经过一段时间的琢磨和设计 该平台已经完成了基本的功能需求 大家可以来一起看一下 或许对你的设计有所帮助 视频详解 基于vue2开发的毕业设计 时光云平台 访问连接 点击进入 前端的登录首页界面 前台注册页面展示界面 登录进去的首页界面展
  • java访问其他机器上的mysql_java实现从一台机器访问另外一台机器的mysql数据库

    烙印99 驱动程序名 String driver com mysql jdbc Driver URL指向要访问的数据库名scutcs String url jdbc mysql 另一台电脑的ip地址 3306 数据库名 MySQL配置时的用
  • Java 内部是如何判断Map中的两个键是否一样

    Java 内部是如何判断Map中的两个键是否一样 为什么会问这个问题呢 源于我在慕课网看到相关的教学视频 http www imooc com video 5987 中有出现这个判断方法 是关于重写hashcode 和equals 方法 但
  • [TFF学习]官方教程jupyter运行记录_联邦学习之图像分类任务_1

    chapter 1 检查系统环境和第三方库是否安装完毕 在Web服务器 gui应用程序和jupyter笔记本运行python程序时 出现 RuntimeError This event loop is already running 可能是
  • python“ModuleNotFoundError: No module named 模块名” 错误的一种情况

    跨目录导包 运行python脚本时 有时出现如题的错误 原因一般是用了os sys两个模块 而没有理解清楚两者的关系 os是关于本地系统的操作 sys是关于python环境的操作 如模块搜索路径配置 一种情况如下 import sys os
  • 又回老家了

    不愿意在朝阳区当天选打工人了 于是回老家了 找了份点云渲染的工作 月薪两万五 下月入职 本打算从小公司起步 做ue 没想到 现有的两套框架和十几个成熟项目不让碰 大概是提防着我吧 只让攻坚克难 做cesium for ue和热更新 想想也没
  • UE4.25 Slate源码解读

    优质资源分享 学习路线指引 点击解锁 知识定位 人群定位 Python实战微信订餐小程序 进阶级 本课程是python flask 微信小程序的完美结合 从项目搭建到腾讯云部署上线 打造一个全栈订餐系统 Python量化交易实战 入门级 手
  • echarts中的legend属性

    legend orient vertical right 0 top 15 icon circle 小圆点 itemWidth 8 itemHeight 8 itemGap 15 间隔 formatter function params l
  • 潜在结果框架(Potential outcomes)与工具变量(Instrumental variable)介绍

    Potential outcomes framework 什么是potential outcome呢 考虑在医学中 X 0表示不吃药 X 1表示吃药 那么很显然 一个人是没有办法同时吃药与不吃药的 所以我们只能够观测到其中的一个结果 即 Y
  • Javaweb学生信息管理系统(Mysql+JSP+MVC+CSS)

    目录 一 项目介绍 二 运行效果 1 登录界面 2 主界面 点击学号修改学生信息 3 增加学生界面 编辑 三 项目目录结构 四 代码展示 1 jsp及css代码 登录界面代码 login jsp 登录界面css login css 注册用户
  • js深拷贝

    js深拷贝的几种方式 概念介绍 深拷贝 在堆内存中重新开辟一个存储空间 完全克隆一个一模一样的对象 浅拷贝 不在堆内存中重新开辟空间 只复制栈内存中的引用地址 本质上两个对象 数组 依然指向同一块存储空间 一 递归方式 推荐 项目中最安全最
  • 4款超实用绘图软件

    对于新手来说 很多人认为绘图软件需要一定基础的设计功底 但其实也不完全是 在网上有很多非常实用的绘图软件可以迅速的帮助我们解决工作中的绘图问题 以下4款是这两年我的小伙伴们用的最多的 推荐给大家 亿图图示 亿图图示是我用的比较多的一个绘图软
  • windows的磁盘操作之九——区分本地磁盘与移动硬盘

    转载自 windows的磁盘操作之九 区分本地磁盘与移动硬盘 bunny技术坊的技术博客 51CTO博客 原文如下 最近碰到了个新问题 记录下来作为windows的磁盘操作那个系列的续篇吧 一些时候我们的程序需要区分本地存储设备和USB存储