不同WINDOWS平台下磁盘逻辑扇区的直接读写
  关键字:VWIN32、中断、DeviceIoControl
  一、概述
   在DOS操作系统下,通过BIOS的INT13、DOS的INT25(绝对读)、INT26(绝对写)等功能调用实现对磁盘逻辑扇区或物理扇区的读写是很方便的,C语言中还有对应上述功能调用的函数:biosdisk、absread和abswrite等。但在WINDOWS操作系统下编写WIN32应用程序时却再也不能直接使用上述的中断调用或函数了。那么,在WINDOWS操作系统下能不能实现磁盘扇区的直接读写呢?如何实现磁盘扇区的读写呢?为了解决这些问题,笔者查阅了一些相关资料后发现,WINDOWS操作系统也提供了读写磁盘扇区的方法,只是在不同的版本中有着不同的方式和使用限制。最后,笔者编写了一个磁盘扇区直接读写类,不敢独专,特提供出来,希望能对大家有所帮助。
   注:这里INT13表示INT 13H,其它类同。
  二、一个读取软盘扇区的例子
   WINDOWS操作系统对所有的存储设备实行了统一管理,而且为了安全起见,操作系统还不允许在WIN32应用程序(工作在Ring3级)中直接调用中断功能,如INT13、INT21、INT25、INT26等。但它同时也提供了一些服务来弥补这种缺憾,在WIN95/98中,VWIN32服务就是其中一种。VWIN32服务是通过一个VXD来实现的,它提供了设备IO功能,通过它,使用API函数DeviceIoControl便可以实现WIN32应用程序和磁盘设备驱动程序间的通信,从而实现对磁盘的存取。VWIN32提供的服务是一系列的控制命令字,它们实现诸如DOS操作系统下的INT13、INT25、INT26和INT21等功能调用。下面是它定义的一些控制命令字:
   VWIN32_DIOC_DOS_IOCTL (1) 实现INT21 功能
   VWIN32_DIOC_DOS_INT25 (2) 实现INT25 功能
   VWIN32_DIOC_DOS_INT26 (3) 实现INT26 功能 
   VWIN32_DIOC_DOS_INT13 (4) 实现INT13 功能
   VWIN32_DIOC_DOS_DRIVEINFO (6) 实现INT21 730x 功能 
  如果要对磁盘进行读写,只要使用DeviceIoControl执行相应命令即可,下面的例子用来读取软盘的一个扇区(使用INT13):
   第一步:打开VWIN32服务,HANDLE hDev=CreateFile("\\\\.\\VWIN32",0,0,0,0,FILE_FLAG_DELETE_ON_CLOSE,NULL);
   第二步:填充中断所用到的相关寄存器。这里将寄存器放在一个结构中,结构定义如下(有关INT13使用的寄存器情况,请参阅相关资料):
   typedef struct INT13Regs{
   PVOID buffer; // ebx 寄存器
   BYTE Drive; // 盘号 dl
  BYTE Head; //头号 dh
  WORD EDX_High; // edx 寄存器 
  BYTE Sector; //始扇区 cl
  BYTE Track; //道号 ch
  WORD ECX_High; //ecx 寄存器
  BYTE Number; //要读写的扇取数 al
  BYTE CMD; //命令:2--读,3--写,5--格式化 ah
  WORD EAX_High; //eax 寄存器
  DWORD EDI; // edi 寄存器
   DWORD ESI; // esi
   DWORD EFLAG; // flags
   }INT13_REGISTERS;
   unsigned char Buffer[512];//定义缓冲区,放置读取扇区数据
   INT13_REGISTERS reg={0};//定义寄存器结构变量
   
   reg.buffer =(void *)Buffer;
   reg.Drive =0;//0-软盘A 1-软盘B 0x80-硬盘c
   reg.Head =0;
   reg.Track=0;
   reg.Sector=1;
   reg.Number=1;
   reg.CMD=2; //读
   第三步:调用设备IO API函数DeviceIoControl执行4号命令(即VWIN32_DIOC_DOS_INT13), BOOL b_ret=DeviceIoControl(hDev,4,&reg,sizeof(INT13_REGISTERS),&reg,sizeof(INT13_REGISTERS),&lpRet,0);
  如果其返回值不等于零,调用成功,进一步处理....否则调用失败。
   第四步:关闭服务,CloseHandle(hDev);
  三、限制或局限
   上面是使用INT13读取软盘扇区的完整步骤,在WIN95/98下它是可以工作的。那么,是否将上面的寄存器结构中的Drive置为0x80就可以读取逻辑硬盘C盘的扇区了呢?回答是否定的。INT13用来存取硬盘的功能在WINDOWS中被忽略了。另外,INT25、INT26虽然可以存取硬盘,但是它们不能工作在FAT32格式的硬盘上。下面的列表将详细列举与磁盘操作相关的中断调用的限制情况(不特殊说明,指的是在WIN95/98操作系统下):
   中断功能 限制及使用情况
   INT13 不可以读写硬盘,仅支持软盘
   INT25/INT26 不可以读/写FAT32硬盘,支持FAT12、FAT16
   INT21(440DH-41H/61H) 不可用(文档资料中说支持FAT12、FAT16、FAT32,实际上没有实现)
   INT21(7305H) 可以读写软盘、硬盘,支持FAT12、FAT16、FAT32,但要求WIN95OSR2及以后版本
   值得一提的是上表中的INT21--7305H功能是专门提供用来支持FAT32的,并且用来替换INT25/INT26,对应的控制命令字是6(即VWIN32_DIOC_DOS_DRIVEINFO),它和INT13、INT25、INT26等中断功能的一个显著区别是:它不使用寄存器来传递参数(INT21--440DH-41H/61H类同),而是使用一个称为DISKIO的结构,寄存器EBX用来保存指向该结构的地址。DISKIO的定义如下:
   typedef struct _DISKIO {
   DWORD dwStartSector; // 要读写的起始扇区号
   WORD wSectors; // 要读写的扇区数
   DWORD dwBuffer; // 用来保存读/写数据的缓冲区
   }DISKIO, * PDISKIO;
  另外,在使用该功能时还需要特别设置一些寄存器,如ECX必须为-1,用ESI来表示读写。下面的例子是使用该功能来实现上面的例子功能,即读软盘A的一个扇区。首先定义一个新的寄存器结构供本例使用:
   typedef struct _DIOC_REGISTERS{ 
   DWORD EBX; 
   DWORD EDX; 
   DWORD ECX; 
   DWORD EAX; 
   DWORD EDI; 
   DWORD ESI; 
   DWORD Flags; 
   }DIOC_REGISTERS; 
  其实该结构和上面的INT13_REGISTERS是一样的,只不过INT13_REGISTERS将寄存器细分开了,可读性更强些。本例从步骤上说和上面的例子相同,只有寄存器设置一步在内容上有差异。
   第一步:打开VWIN32服务。
   第二步:设置寄存器。
   DIOC_REGISTERS reg = {0};
   DISKIO dio;
   unsigned char Buffer[512];
   //设置参数结构
   dio.dwStartSector = 0;//注意:和上例不同,不是1,从0开始编号
   dio.wSectors = 1;
   dio.dwBuffer = (DWORD)Buffer;
   //设置寄存器
   reg.EAX = 0x7305; //功能上类似于INT25,绝对读 
   reg.EBX = (DWORD)&dio;//参数结构的地址
   reg.ECX = -1;//必须是-1 
   reg.EDX = 1; //注意:和上例不同,驱动器编号变了,0--缺省 1--A、2--B、3--C 
   reg.ESI = 0; //ESI的bit0表示读写,0--读、1--写
   在写状态时SI的bit1--bit12,bit15必须是0,bit13、bit14、bit15共同来表示所写数据的类型,具体见下表:
   15 14 13 类型描述
   0 0 0 其它或不知道. 
   0 0 1 FAT数据 
   0 1 0 目录数据 
   0 1 1 一般数据 
   1 x x 保留。bit15必须是0 
   第三步:调用API。BOOL b_ret=DeviceIoControl(hDev,6,&reg, sizeof(DIOC_REGISTERS),&reg,sizeof(DIOC_REGISTERS),&cb,0);
   第四步:关闭服务。
  可以发现,两种方法读到的数据完全一致。
  四、WIN2000中的磁盘扇区读写
   在WINNT和WIN2000中磁盘被看做一种标准设备,可以使用CreateFile象打开文件一样打开并存取。CreateFile支持两种方式的磁盘设备--逻辑磁盘(格式为"\\.\C:")和物理磁盘(格式为"\\.\PHYSICALDRIVEx",其中x为数字),例如打开A:盘进行读取操作,只要这样:
   HANDLE hDev=CreateFile("\\\\.\\A:",GENERIC_READ,FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0);
  如果得到的句柄有效,就可以使用ReadFile来读取了,
   ReadFile(hDev,Buffer,512,&dwRet,0);
  读取结束要关闭该句柄,
   CloseHandle(hDev);
  这比WIN95/98下的磁盘扇区读取方便多了。
   另外,上面的例子是操作逻辑磁盘的,它包括软驱、硬盘分区等;物理磁盘指的是实际的硬盘,它不关心该硬盘被分成几个区,硬盘的编号是从0开始的,"\\.\PHYSICALDRIVE0"表示第一块硬盘,其它依此类推。大家可能马上会想起,利用这种机制可以对硬盘的分区表进行存取了。确实如此,此时便可以对硬盘的主引导扇区(独立存在的一个扇区,包含分区表信息,不同于磁盘分区的BOOT区)进行操作了。
   unsigned char Buffer[512]={0};
   HANDLE hDev=CreateFile("\\\\.\\PHYSICALDRIVE0",GENERIC_WRITE,FILE_SHARE_WRITE,0,OPEN_EXISTING,0,0);
   WriteFile(hDev,Buffer,512,&dwRet,0);
   CloseHandle(hDev);
  危险!!!千万别这么做!!!
  五、一个自适应的磁盘读写类
   由上面的例子可以看出,不同的操作系统下对磁盘扇区的读写有不同的方式,为了能够在各类操作系统下能够使用统一的方法读写磁盘扇区,特设计了一个通用类。该类的设计思想如下:首先编写各类操作系统下的磁盘扇区存取函数,然后通过GetVersionEx来判断操作系统,进而选取对应的函数来实现磁盘扇区的读写。由上面的分析可知,WINDOWS操作系统对INT13的支持是最差的,所以在这里只使用INT25、INT26、INT21--7305等中断调用来实现。类的定义如下:
  class CDiskInfo{
  public:
  CDiskInfo();
  ~CDiskInfo();
  private:
  HANDLE hDev;
   DWORD dwCurrentPlatform;
   void GetPlatform(); //取得操作系统,并存入变量dwCurrentPlatform
   
  BOOL Win2000_AccessSectors(WORD CMD,BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);//用于WIN2000、WINNT等操作系统,
  BOOL Int25_ReadSectors(BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);
   BOOL Int26_WriteSectors(BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);//用于WIN95以前的操作系统
   BOOL Int21_AccessSectors(WORD CMD,BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);//7305功能实现,用于WIN95OSR2、WIN98等操作系统
  public:
   //对外统一提供Read和Write操作,类内部根据平台选用适合的函数调用
  BOOL ReadSectors(BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);
  BOOL WriteSectors(BYTE bDrive,DWORD dwStartSector,WORD wSectors,LPBYTE lpSectBuff);
  };
  该类对外提供了两个接口,即ReadSectors和WriteSectors,其参数是一样的,分别是要读写的磁盘编号bDrive,要存取磁盘的开始扇区号dwStartSector,要读取的扇区数wSectors和读写扇区数据的缓冲区lpSectBuff。这里磁盘编号是从1开始的,即1代表A:,2代表B:,3代表C:,依此类推。扇区的编号从0开始。使用时也很简单,只要作如下声明即可:
   BYTE Buffer[1024]; 
   CDiskInfo A;
   BOOL bRet=A.ReadSectors(1,0,2,Buffer);
   详细情况见附带的类文件及测试程序。
  六、补充说明
   严格来说,在对磁盘进行读写时,应该遵循以下顺序:打开设备(WIN95/98下为VWIN32服务,WIN2000下为磁盘设备)、锁卷、验证卷的有效性、读/写、开锁卷、关闭设备。这里为了描述上的简洁,忽略了锁卷/开锁卷及验证有效性等操作。有兴趣的朋友可以自行添加。
   另外,该类仅实现了逻辑驱动器的读写,要想实现诸如对物理硬盘的主引导扇区的读写,还需要其它技术,如thunk技术,即编写两个动态库,一个是WIN32动态库,一个是WIN16动态库(thunk技术只可以用动态库实现),其中WIN16动态库转到DPMI模式,调用INT13(或者扩展INT13)来实现物理磁盘扇区的读写。有关thunk技术请参阅相关文档资料。
   所有的例子在WIN98、WIN2000操作系统、VC6集成环境下调试通过。

不同WINDOWS平台下磁盘逻辑扇区的直接读写的更多相关文章

  1. Windows平台下的读写锁

    Windows平台下的读写锁简单介绍Windows平台下的读写锁以及实现.背景介绍Windows在Vista 和 Server2008以后才开始提供读写锁API,即SRW系列函数(Initialize ...

  2. WebKit 在 Windows 平台下编译小结

    虽然WebKit 已经越来越多的被广大程序员接受,但其编译过程却非常之痛苦.下面将我编译WebKit 代码的经验与大家分享. 1) 获取WebKit 源代码 WebKit 源代码是使用Subversi ...

  3. Windows平台下利用APM来做负载均衡方案 - 负载均衡(下)

    概述 我们在上一篇Windows平台分布式架构实践 - 负载均衡中讨论了Windows平台下通过NLB(Network Load Balancer) 来实现网站的负载均衡,并且通过压力测试演示了它的效 ...

  4. windows平台下基于VisualStudio的Clang安装和配置

    LLVM 是一个开源的编译器架构,它已经被成功应用到多个应用领域.Clang是 LLVM 的一个编译器前端,它目前支持 C, C++, Objective-C 以及 Objective-C++ 等编程 ...

  5. [转]Windows平台下安装Hadoop

    1.安装JDK1.6或更高版本 官网下载JDK,安装时注意,最好不要安装到带有空格的路径名下,例如:Programe Files,否则在配置Hadoop的配置文件时会找不到JDK(按相关说法,配置文件 ...

  6. MongoDB学习总结(一) —— Windows平台下安装

    > 基本概念 MongoDB是一个基于分布式文件存储的开源数据库系统,皆在为WEB应用提供可扩展的高性能数据存储解决方案.MongoDB将数据存储为一个文档,数据结构由键值key=>val ...

  7. windows平台下基于QT和OpenCV搭建图像处理平台

        在之前的博客中,已经分别比较详细地阐述了"windows平台下基于VS和OpenCV"以及"Linux平台下基于QT和OpenCV"搭建图像处理框架,并 ...

  8. Windows平台下的内存泄漏检测

    在C/C++中内存泄漏是一个不可避免的问题,很多新手甚至有许多老手也会犯这样的错误,下面说明一下在windows平台下如何检测内存泄漏. 在windows平台下内存泄漏检测的原理大致如下. 1. 在分 ...

  9. Spotlight on Mysql在Windows平台下的安装及使用简介

    Spotlight on Mysql在Windows平台下的安装及使用简介   by:授客 QQ:1033553122 1.   测试环境 Win7 64位 mysql-connector-odbc- ...

随机推荐

  1. JavaBean,List,Map,json格式之间转化方式

    public class TestBean { private String id; private String name; private String password; public Stri ...

  2. java之过滤器Filter

    Java三大器之过滤器(Filter)的工作原理和代码演示   一.Filter简介 Filter也称之为过滤器,它是Servlet技术中最激动人心的技术之一,WEB开发人员通过Filter技术,对w ...

  3. iOS-Runtime之关于页面跳转的捷径【Runtime获取当前ViewController】

    写在前面 在我们操作页面跳转时,如果当前的类不是UIViewcontroller(下面用VC表示),你会不会写一个代理,或者block给VC传递信息,然后在VC里面进行 ///假如targetVc是将 ...

  4. 为Ghost博客扩展代码高亮、数学公式、页面统计、评论

    前几天捣鼓了一下博客首页,接下来再丰富一下博客页面的功能与内容.由于我所使用的Ghost博客专注于轻量简洁,因此标题中提到的功能在Ghost中默认均不支持.下面将逐个介绍一下如何为Ghost扩展这些功 ...

  5. RHEL7.2安装

    先在系统启动的时候按下Del键(有些系统是F2键)进入BIOS,设置从光盘启动. 系统只有2个USB口时,1个要接光驱,另外1个口不能同时接键盘和鼠标,可以接1个USB集线器,键盘和鼠标同时接入到集线 ...

  6. JDBC学习笔记(三)

    获取数据库的元信息metadata,里面有数据库特性的描述信息,如是否支持事务,是否支持批处理等. Connection conn = DriverManager.getConnection(url, ...

  7. POJ [P3660] Cow Contest

    传递闭包经典应用 奶牛的名次能确定当且仅当在它前面的牛数+在他后面的牛数==n-1 在他前面和后面的牛数可以转化成求完传递闭包后的出度和入度 #include <iostream> #in ...

  8. iconfont 怎么在项目中使用图标库

    iconfont是很多设计以及前后端人员编写页面时经常用到的网站,阿里不仅为我们提供了免费的图标库,并且有一套完整的图标库体系.很多初学者只知道从图标库中下载图标放入项目中,但在实际项目应用中,过多的 ...

  9. linux下ACE的编译与安装

    1.环境变量的设置vim /etc/profile 2.然后输入export ACE_ROOT=/root/ACE/ACE_wrappers export MPC_ROOT=$ACE_ROOT/MPC ...

  10. 异步任务利器Celery(一)介绍

    django项目开发中遇到过一些问题,发送请求后服务器要进行一系列耗时非常长的操作,用户要等待很久的时间.可不可以立刻对用户返回响应,然后在后台运行那些操作呢? crontab定时任务很难达到这样的要 ...