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

本文介绍如何利用DeviceIoControl实现软盘的全盘复制,通过具体示例展示了打开磁盘、获取磁盘参数、读写磁盘等核心步骤,并讨论了磁盘操作的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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,我们已经探讨过的。(王朝网络 wangchao.net.cn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值