实战DeviceIoControl之三:制作磁盘镜像文件

实战DeviceIoControl之三:制作磁盘镜像文件

Q DOS命令DISKCOPY给我很深的印象,现在也有许多“克隆” 软件,可以对磁盘进行全盘复制。我想,要制作磁盘镜像文件,DeviceIoControl应该很有用武之地吧?

A 是的。这里举一个制作软盘镜像文件,功能类似于“DISKCOPY”的例子。
本例实现其功能的核心代码如下:

//  打开磁盘
HANDLE OpenDisk(LPCTSTR filename)
{
 HANDLE hDisk;

 
//  打开设备
 hDisk  =  ::CreateFile(filename,    //  文件名
  GENERIC_READ  |  GENERIC_WRITE,   //  读写方式
  FILE_SHARE_READ  |  FILE_SHARE_WRITE,  //  共享方式
  NULL,      //  默认的安全描述符
  OPEN_EXISTING,     //  创建方式
   0 ,      //  不需设置文件属性
  NULL);      //  不需参照模板文件

 
return  hDisk;
}

//  获取磁盘参数
BOOL GetDiskGeometry(HANDLE hDisk, PDISK_GEOMETRY lpGeometry)
{
    DWORD dwOutBytes;
 BOOL bResult;

 
//  用IOCTL_DISK_GET_DRIVE_GEOMETRY取磁盘参数
 bResult  =  ::DeviceIoControl(hDisk,   //  设备句柄
  IOCTL_DISK_GET_DRIVE_GEOMETRY,   //  取磁盘参数
  NULL,  0 ,     //  不需要输入数据
  lpGeometry,  sizeof (DISK_GEOMETRY),  //  输出数据缓冲区
   & dwOutBytes,     //  输出数据长度
  (LPOVERLAPPED)NULL);    //  用同步I/O

 
return  bResult;
}

//  从指定磁道开始读磁盘
BOOL ReadTracks(HANDLE hDisk, PDISK_GEOMETRY lpGeometry, LPVOID pBuf, DWORD dwStartCylinder, DWORD 

dwCylinderNumber)
{
    DWORD VirtBufSize;
    DWORD BytesRead;
                
 
//  大小
 VirtBufSize  =   lpGeometry -> TracksPerCylinder  *  lpGeometry -> SectorsPerTrack  *  lpGeometry -> BytesPerSector;

 
//  偏移
 ::SetFilePointer(hDisk, VirtBufSize * dwStartCylinder, NULL, FILE_BEGIN);

 
return  ::ReadFile(hDisk, pBuf, VirtBufSize * dwCylinderNumber,  & BytesRead, NULL);
}

//  从指定磁道开始写磁盘
BOOL WriteTracks(HANDLE hDisk, PDISK_GEOMETRY lpGeometry, LPVOID pBuf, DWORD dwStartCylinder, DWORD 

dwCylinderNumber)
{
    DWORD VirtBufSize;
    DWORD BytesWritten;

 
//  大小
 VirtBufSize  =   lpGeometry -> TracksPerCylinder  *  lpGeometry -> SectorsPerTrack  *  lpGeometry -> BytesPerSector;

 
//  偏移
 ::SetFilePointer(hDisk, VirtBufSize * dwStartCylinder, NULL, FILE_BEGIN);

    
return  ::WriteFile(hDisk, pBuf, VirtBufSize * dwCylinderNumber,  & BytesWritten, NULL);
}

//  从指定磁道开始格式化磁盘
BOOL LowLevelFormatTracks(HANDLE hDisk, PDISK_GEOMETRY lpGeometry, DWORD dwStartCylinder, DWORD 

dwCylinderNumber)
{
    FORMAT_PARAMETERS FormatParameters;
    PBAD_TRACK_NUMBER lpBadTrack;
    DWORD dwOutBytes;
    DWORD dwBufSize;
    BOOL bResult;

    FormatParameters.MediaType 
=  lpGeometry -> MediaType;
    FormatParameters.StartCylinderNumber 
=  dwStartCylinder;
    FormatParameters.EndCylinderNumber 
=  dwStartCylinder  +  dwCylinderNumber  -   1 ;
    FormatParameters.StartHeadNumber 
=   0 ;
    FormatParameters.EndHeadNumber 
=  lpGeometry -> TracksPerCylinder  -   1 ;

    dwBufSize 
=  lpGeometry -> TracksPerCylinder  *   sizeof (BAD_TRACK_NUMBER);

    lpBadTrack 
=  (PBAD_TRACK_NUMBER)  new  BYTE[dwBufSize];
                
 
//  用IOCTL_DISK_FORMAT_TRACKS对连续磁道进行低级格式化
 bResult  =  ::DeviceIoControl(hDisk,    //  设备句柄
  IOCTL_DISK_FORMAT_TRACKS,    //  低级格式化
   & FormatParameters,  sizeof (FormatParameters),  //  输入数据缓冲区
  lpBadTrack, dwBufSize,     //  输出数据缓冲区
   & dwOutBytes,      //  输出数据长度
  (LPOVERLAPPED)NULL);     //  用同步I/O

    delete lpBadTrack;

    
return  bResult;
}

//  将卷锁定
BOOL LockVolume(HANDLE hDisk)
{
    DWORD dwOutBytes;
    BOOL bResult;

 
//  用FSCTL_LOCK_VOLUME锁卷
 bResult  =  ::DeviceIoControl(hDisk,   //  设备句柄
  FSCTL_LOCK_VOLUME,    //  锁卷
  NULL,  0 ,     //  不需要输入数据
  NULL,  0 ,     //  不需要输出数据
   & dwOutBytes,     //  输出数据长度
  (LPOVERLAPPED)NULL);    //  用同步I/O

    
return  bResult;
}

//  将卷解锁
BOOL UnlockVolume(HANDLE hDisk)
{
    DWORD dwOutBytes;
    BOOL bResult;

 
//  用FSCTL_UNLOCK_VOLUME开卷锁
 bResult  =  ::DeviceIoControl(hDisk,   //  设备句柄
  FSCTL_UNLOCK_VOLUME,    //  开卷锁
  NULL,  0 ,     //  不需要输入数据
  NULL,  0 ,     //  不需要输出数据
   & dwOutBytes,     //  输出数据长度
  (LPOVERLAPPED)NULL);    //  用同步I/O

    
return  bResult;
}

//  将卷卸下
//  该操作使系统重新辨识磁盘,等效于重新插盘
BOOL DismountVolume(HANDLE hDisk)
{
    DWORD dwOutBytes;
    BOOL bResult;

 
//  用FSCTL_DISMOUNT_VOLUME卸卷
 bResult  =  ::DeviceIoControl(hDisk,   //  设备句柄
  FSCTL_DISMOUNT_VOLUME,    //  卸卷
  NULL,  0 ,     //  不需要输入数据
  NULL,  0 ,     //  不需要输出数据
   & dwOutBytes,     //  输出数据长度
  (LPOVERLAPPED)NULL);    //  用同步I/O

    
return  bResult;
}

将软盘保存成镜像文件的步骤简单描述为:
1、创建空的镜像文件。
2、调用OpenDisk打开软盘。成功转3,失败转8。
3、调用LockVolume将卷锁定。成功转4,失败转7。
4、调用GetDiskGeometry获取参数。成功转5,失败转6。
5、将磁盘参数写入镜像文件作为文件头。调用ReadTracks按柱面读出数据,保存在镜像文件中。循环次数等于柱面数。
6、调用UnlockVolume将卷解锁。
7、调用CloseDisk关闭软盘。
8、关闭镜像文件。

将镜像文件载入软盘的步骤简单描述为:
1、打开镜像文件。
2、调用OpenDisk打开软盘。成功转3,失败转11。
3、调用LockVolume将卷锁定。成功转4,失败转10。
4、调用GetDiskGeometry获取参数。成功转5,失败转9。
5、从镜像文件中读出文件头,判断两个磁盘参数是否一致。不一致转6,否则转7。
6、调用LowLevelFormatTracks按柱面格式化软盘。循环次数等于柱面数。成功转7,失败转8。
7、从镜像文件中读出数据,并调用WriteTracks按柱面写入磁盘。循环次数等于柱面数。
8、调用DismountVolume将卷卸下。
9、调用UnlockVolume将卷解锁。
10、调用CloseDisk关闭软盘。
11、关闭镜像文件。


Q 我注意到,磁盘读写和格式化是按柱面进行的,有什么道理吗?

A 没有特别的原因,只是因为在这个例子中可以方便地显示处理进度。
有一点需要特别提及,按绝对地址读写磁盘数据时,“最小单位”是扇区,地址一定要与扇区对齐,长度也要等于扇区长度的整数倍。比如,每扇区512字节,那么起始地址和数据长度都应能被512整除才行。


Q 我忽然产生了一个伟大的想法,用绝对地址读写的方式使用磁盘,包括U盘啦,MO啦,而不是用现成的文件系统,那不是可以将数据保密了吗?

A 当然,只要你喜欢。可千万别在你的系统盘上做试验,否则......可别怪bhw98没有提醒过你喔!


Q 我知道怎么测试光驱的传输速度了,就用上面的方法,读出一定长度数据,除以所需时间,应该可以吧?

A 可以。但取光盘参数时要用IOCTL_STORAGE_GET_MEDIA_TYPES_EX,我们已经探讨过的。

你可能感兴趣的:(实战DeviceIoControl之三:制作磁盘镜像文件)