如何用delphi读到硬盘扇区(50分)

  • 主题发起人 主题发起人 yanghoyu
  • 开始时间 开始时间
Y

yanghoyu

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