如何使用C++将包含多个分区的映像写入Windows上的USB闪存驱动器

How to write an image containing multiple partitions to a USB flash drive on Windows using C++

本文关键字:Windows 上的 USB 驱动器 闪存 映像 C++ 何使用 包含多 分区      更新时间:2023-10-16

在Windows上,您只能在可移动媒体上看到第一个分区。我想写一个C++程序,可以写一个包含MBR和2个数据分区的图像到USB闪存驱动器。我不需要第二个分区在Windows中可见-我只需要能够从Windows/C++将这个原始映像写入USB闪存驱动器,这样以后在Linux上运行时,就可以看到这两个分区。

我读过关于安装过滤器驱动程序的文章,该驱动程序最终会将可移动介质视为固定介质,这对阅读来说很好,但我只想在对用户的电脑干扰最小的情况下写入此图像。我可以从第一个扇区开始访问原始USB驱动器,然后只写入MBR,然后写入两个数据分区吗?

如果您有足够的权限,您可以打开一个句柄,使用文件名为\.PhysicalDrive2CreateFile直接写入USB驱动器。

您需要计算出物理驱动器的编号。如果您有驱动器号,可以使用类似\?D:的文件名打开卷句柄,然后使用IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS来确定与卷关联的物理驱动器号。

您可能需要先卸除现有卷(如果有的话)。我不确定什么是最好的方法,但您可以尝试使用IOCTL_VOLUME_OFFLINE

编辑:这里有一些我用于磁盘成像的代码,尽管我不记得我是否曾在U盘上尝试过。公共领域,但没有明示或暗示的保证,等(仔细查看代码,我注意到我没有明确检查扇区大小,以确保缓冲区和读/写操作正确对齐。这在实践中应该不是问题,因为常用的最大扇区大小是4K,这也是Windows上的内存页大小。然而,在生产代码中,您应该始终明确确定e有问题的设备,并相应地确保正确对齐。)

#define _WIN32_WINNT 0x0501
#include <windows.h>
#include <stdio.h>
#define dump_buffersize_megs 16
#define dump_buffersize (dump_buffersize_megs * 1024 * 1024)
#define dump_workingsetsize ((dump_buffersize_megs + 1) * 1024 * 1024)
DWORD save(const wchar_t * source_device_name, const wchar_t * filename) {
  DWORD err;
  HANDLE hdevice, houtput;
  DWORD bytes_to_transfer, byte_count;
  GET_LENGTH_INFORMATION source_disklength;
  DISK_GEOMETRY source_diskgeometry;
  LARGE_INTEGER offset;
  OVERLAPPED overlapped;
  BYTE * buffer;
  if (!SetProcessWorkingSetSize(GetCurrentProcess(), dump_workingsetsize, dump_workingsetsize)) 
  {
    err = GetLastError();
    printf("Error %u trying to expand working set.n", err);
    return err;
  }
  buffer = VirtualAlloc(NULL, dump_buffersize, MEM_COMMIT, PAGE_READWRITE);
  if (buffer == NULL)
  {
    err = GetLastError();
    printf("Error %u trying to allocate buffer.n", err);
    return err;
  }
  if (!VirtualLock(buffer, dump_buffersize))
  {
    err = GetLastError();
    printf("Error %u trying to lock buffer.n", err);
    return err;
  }
  hdevice = CreateFile
    (
    source_device_name,
    GENERIC_READ,
    0,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_NO_BUFFERING,
    NULL
    );
  if (hdevice == INVALID_HANDLE_VALUE) {
    err = GetLastError();
    fprintf(stderr, "Error %u opening input device.n", err);
    return err;
  }
  if (!DeviceIoControl
    (
    hdevice,
    FSCTL_LOCK_VOLUME,
    NULL,
    0,
    NULL,
    0,
    &byte_count,
    NULL
    ))
  {
    err = GetLastError();
    fprintf(stderr, "Error %u locking input volume.n", err);
    return err;
  }
  if (!DeviceIoControl
    (
    hdevice,
    IOCTL_DISK_GET_DRIVE_GEOMETRY,
    NULL,
    0,
    &source_diskgeometry,
    sizeof(source_diskgeometry),
    &byte_count,
    NULL
    ))
  {
    err = GetLastError();
    fprintf(stderr, "Error %u getting device geometry.n", err);
    return err;
  }
  switch (source_diskgeometry.MediaType)
  {
  case Unknown:
  case RemovableMedia:
  case FixedMedia:
    if (!DeviceIoControl
      (
      hdevice,
      IOCTL_DISK_GET_LENGTH_INFO,
      NULL,
      0,
      &source_disklength,
      sizeof(source_disklength),
      &byte_count,
      NULL
      ))
    {
      err = GetLastError();
      fprintf(stderr, "Error %u getting input device length.n", err);
      return err;
    }
    fprintf(stderr, "nInput disk has %I64i bytes.nn", source_disklength.Length.QuadPart);
    break;
  default:
    source_disklength.Length.QuadPart = 
      source_diskgeometry.Cylinders.QuadPart *
      source_diskgeometry.TracksPerCylinder *
      source_diskgeometry.SectorsPerTrack *
      source_diskgeometry.BytesPerSector;
    fprintf(stderr, 
      "n"
      "Input device appears to be a floppy disk.  WARNING: if this is not an"
      "floppy disk the calculated size will probably be incorrect, resultingn"
      "in an incomplete copy.n"
      "n"
      "Input disk has %I64i bytes.n"
      "n", 
      source_disklength.Length.QuadPart);
    break;
  }
  houtput = CreateFile
    (
    filename,
    GENERIC_WRITE,
    0,
    NULL,
    CREATE_ALWAYS,
    FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
    NULL
    );
  if (houtput == INVALID_HANDLE_VALUE)
  {
    err = GetLastError();
    fprintf(stderr, "Error %u creating output file.n", err);
    return err;
  }
  offset.QuadPart = 0;
  overlapped.hEvent = 0;
  for (;;) 
  {
    overlapped.Offset = offset.LowPart;
    overlapped.OffsetHigh = offset.HighPart;
    if (source_disklength.Length.QuadPart - offset.QuadPart < dump_buffersize) 
    {
      bytes_to_transfer = (DWORD)(source_disklength.Length.QuadPart - offset.QuadPart);
      if (bytes_to_transfer == 0) break;
    }
    else
    {
      bytes_to_transfer = dump_buffersize;
    }
    if (!ReadFile(hdevice, buffer, bytes_to_transfer, NULL, &overlapped)) 
    {
      err = GetLastError();
      printf("Error %u initiating read from input disk.n", err);
      return err;
    }
    if (!GetOverlappedResult(hdevice, &overlapped, &byte_count, TRUE)) 
    {
      err = GetLastError();
      printf("Error %u reading from input disk.n", err);
      return err;
    }
    if (byte_count != bytes_to_transfer)
    {
      err = GetLastError();
      printf("Internal error - partial read.  Last error code %u.n", err);
      printf("bytes_to_transfer = %u; byte_count = %u.n", bytes_to_transfer, byte_count);
      if (byte_count == 0) return ERROR_INVALID_FUNCTION;
      bytes_to_transfer = byte_count;
    }
    if (!WriteFile(houtput, buffer, bytes_to_transfer, NULL, &overlapped)) 
    {
      err = GetLastError();
      if (err != ERROR_IO_PENDING)
      {
        printf("Error %u initiating write to output file.n", err);
        return err;
      }
    }
    if (!GetOverlappedResult(houtput, &overlapped, &byte_count, TRUE)) 
    {
      err = GetLastError();
      printf("Error %u writing to output file.n", err);
      return err;
    }
    if (byte_count != bytes_to_transfer)
    {
      printf("Internal error - partial write.n");
      printf("bytes_to_transfer = %u; byte_count = %u.n", bytes_to_transfer, byte_count);
      return ERROR_INVALID_FUNCTION;
    }
    offset.QuadPart += bytes_to_transfer;
  }
  overlapped.Offset = offset.LowPart;
  overlapped.OffsetHigh = offset.HighPart;
  if (!ReadFile(hdevice, buffer, source_diskgeometry.BytesPerSector, NULL, &overlapped)) 
  {
    err = GetLastError();
    if (err == ERROR_HANDLE_EOF)
    {
      printf("Save successfully completed.n");      
      return 0;
    }
    printf("Error %u initiating read from input disk past end of file.n", err);
    return err;
  }
  if (!GetOverlappedResult(hdevice, &overlapped, &byte_count, TRUE)) 
  {
    err = GetLastError();
    if (err == ERROR_HANDLE_EOF)
    {
      printf("Save successfully completed.n");      
      return 0;
    }
    printf("Error %u reading from input disk past end of file.n", err);
    return err;
  }
  if (byte_count == 0)
  {
    printf("Save successfully completed.n"); 
    return 0;
  }
  printf("WARNING: the expected amount of data was successfully copied,n"
         "but end of file not detected on input disk.  The copy mightn"
         "not be complete.");
  return ERROR_MORE_DATA;
}
DWORD write(const wchar_t * filename, const wchar_t * target_device_name) {
  DWORD err;
  HANDLE hinput, houtput;
  WIN32_FILE_ATTRIBUTE_DATA fad;
  DWORD bytes_to_transfer, byte_count;
  LARGE_INTEGER filelength;
  GET_LENGTH_INFORMATION target_disklength;
  DISK_GEOMETRY target_diskgeometry;
  LARGE_INTEGER transfer_length;
  LARGE_INTEGER offset;
  OVERLAPPED overlapped;
  BYTE * buffer;
  if (!SetProcessWorkingSetSize(GetCurrentProcess(), dump_workingsetsize, dump_workingsetsize)) 
  {
    err = GetLastError();
    printf("Error %u trying to expand working set.n", err);
    return err;
  }
  buffer = VirtualAlloc(NULL, dump_buffersize, MEM_COMMIT, PAGE_READWRITE);
  if (buffer == NULL)
  {
    err = GetLastError();
    printf("Error %u trying to allocate buffer.n", err);
    return err;
  }
  if (!VirtualLock(buffer, dump_buffersize))
  {
    err = GetLastError();
    printf("Error %u trying to lock buffer.n", err);
    return err;
  }
  if (!GetFileAttributesEx(filename, GetFileExInfoStandard, &fad)) 
  {
    err = GetLastError();
    fprintf(stderr, "Error %u reading input file attributes.n", err);
    return err;
  }
  filelength.HighPart = fad.nFileSizeHigh;
  filelength.LowPart = fad.nFileSizeLow;
  fprintf(stderr, "nInput file has %I64i bytes.n", filelength.QuadPart);
  hinput = CreateFile
    (
    filename,
    GENERIC_READ,
    0,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED,
    NULL
    );
  if (hinput == INVALID_HANDLE_VALUE)
  {
    err = GetLastError();
    fprintf(stderr, "Error %u opening input file.n", err);
    return err;
  }
  houtput = CreateFile
    (
    target_device_name,
    GENERIC_READ | GENERIC_WRITE,
    0,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_NO_BUFFERING,
    NULL
    );
  if (houtput == INVALID_HANDLE_VALUE) {
    err = GetLastError();
    fprintf(stderr, "Error %u opening output device.n", err);
    return err;
  }
  if (!DeviceIoControl
    (
    houtput,
    FSCTL_LOCK_VOLUME,
    NULL,
    0,
    NULL,
    0,
    &byte_count,
    NULL
    ))
  {
    err = GetLastError();
    fprintf(stderr, "Error %u locking volume.n", err);
    return err;
  }
  if (!DeviceIoControl
    (
    houtput,
    IOCTL_DISK_GET_DRIVE_GEOMETRY,
    NULL,
    0,
    &target_diskgeometry,
    sizeof(target_diskgeometry),
    &byte_count,
    NULL
    ))
  {
    err = GetLastError();
    fprintf(stderr, "Error %u getting output device geometry.n", err);
    return err;
  }
  switch (target_diskgeometry.MediaType)
  {
  case Unknown:
  case RemovableMedia:
  case FixedMedia:
    if (!DeviceIoControl
      (
      houtput,
      IOCTL_DISK_GET_LENGTH_INFO,
      NULL,
      0,
      &target_disklength,
      sizeof(target_disklength),
      &byte_count,
      NULL
      ))
    {
      err = GetLastError();
      fprintf(stderr, "Error %u getting output device length.n", err);
      return err;
    }
    fprintf(stderr, "Output disk has %I64i bytes.nn", target_disklength.Length.QuadPart);
    break;
  default:
    target_disklength.Length.QuadPart = 
      target_diskgeometry.Cylinders.QuadPart *
      target_diskgeometry.TracksPerCylinder *
      target_diskgeometry.SectorsPerTrack *
      target_diskgeometry.BytesPerSector;
    fprintf(stderr, 
      "n"
      "Output device appears to be a floppy disk.  WARNING: if this is not an"
      "floppy disk the calculated output device size is probably incorrect,n"
      "which might result in an incomplete copy.n"
      "n"
      "Output disk has %I64i bytes.n"
      "n", 
      target_disklength.Length.QuadPart);
    break;
  }
  if (filelength.QuadPart == target_disklength.Length.QuadPart)
  {
    transfer_length.QuadPart = filelength.QuadPart;
  }
  else if (filelength.QuadPart < target_disklength.Length.QuadPart)
  {
    fprintf(stderr, "Image is smaller than target.  Part of the target will not be written to.nn");
    transfer_length.QuadPart = filelength.QuadPart;
  }
  else
  {
    fprintf(stderr, "Image is larger than target.  Part of the image will not be copied.nn");
    transfer_length.QuadPart = target_disklength.Length.QuadPart;
  }
  offset.QuadPart = 0;
  overlapped.hEvent = 0;
  for (;;) 
  {
    overlapped.Offset = offset.LowPart;
    overlapped.OffsetHigh = offset.HighPart;
    if (transfer_length.QuadPart - offset.QuadPart < dump_buffersize) 
    {
      bytes_to_transfer = (DWORD)(transfer_length.QuadPart - offset.QuadPart);
      if (bytes_to_transfer == 0) break;
    }
    else
    {
      bytes_to_transfer = dump_buffersize;
    }
    if (!ReadFile(hinput, buffer, bytes_to_transfer, NULL, &overlapped)) 
    {
      err = GetLastError();
      if (err != ERROR_IO_PENDING)
      {
        printf("Error %u initiating read from input file.n", err);
        return err;
      }
    }
    if (!GetOverlappedResult(hinput, &overlapped, &byte_count, TRUE)) 
    {
      err = GetLastError();
      printf("Error %u reading from input file.n", err);
      return err;
    }
    if (byte_count != bytes_to_transfer)
    {
      err = GetLastError();
      printf("Internal error - partial read.  Last error code %u.n", err);
      printf("bytes_to_transfer = %u; byte_count = %u.n", bytes_to_transfer, byte_count);
      if (byte_count == 0) return ERROR_INVALID_FUNCTION;
      bytes_to_transfer = byte_count;
    }
    if (!WriteFile(houtput, buffer, bytes_to_transfer, NULL, &overlapped)) 
    {
      err = GetLastError();
      if (err != ERROR_IO_PENDING)
      {
        printf("Error %u initiating write to output disk.n", err);
        return err;
      }
    }
    if (!GetOverlappedResult(houtput, &overlapped, &byte_count, TRUE)) 
    {
      err = GetLastError();
      printf("Error %u writing to output disk.n", err);
      return err;
    }
    if (byte_count != bytes_to_transfer)
    {
      printf("Internal error - partial write.n");
      printf("bytes_to_transfer = %u; byte_count = %u.n", bytes_to_transfer, byte_count);
      return ERROR_INVALID_FUNCTION;
    }
    offset.QuadPart += bytes_to_transfer;
  }
  printf("Write successfully completed.n");
  return 0;
}
DWORD clone(const wchar_t * source_device_name, const wchar_t * target_device_name) {
  DWORD err;
  HANDLE hinput, houtput;
  DWORD bytes_to_transfer, byte_count;
  GET_LENGTH_INFORMATION source_disklength;
  DISK_GEOMETRY source_diskgeometry;
  GET_LENGTH_INFORMATION target_disklength;
  DISK_GEOMETRY target_diskgeometry;
  LARGE_INTEGER transfer_length;
  LARGE_INTEGER offset;
  OVERLAPPED overlapped;
  BYTE * buffer;
  DWORD result;
  if (!SetProcessWorkingSetSize(GetCurrentProcess(), dump_workingsetsize, dump_workingsetsize)) 
  {
    err = GetLastError();
    printf("Error %u trying to expand working set.n", err);
    return err;
  }
  buffer = VirtualAlloc(NULL, dump_buffersize, MEM_COMMIT, PAGE_READWRITE);
  if (buffer == NULL)
  {
    err = GetLastError();
    printf("Error %u trying to allocate buffer.n", err);
    return err;
  }
  if (!VirtualLock(buffer, dump_buffersize))
  {
    err = GetLastError();
    printf("Error %u trying to lock buffer.n", err);
    return err;
  }
  hinput = CreateFile
    (
    source_device_name,
    GENERIC_READ,
    0,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_NO_BUFFERING,
    NULL
    );
  if (hinput == INVALID_HANDLE_VALUE) {
    err = GetLastError();
    fprintf(stderr, "Error %u opening input device.n", err);
    return err;
  }
  if (!DeviceIoControl
    (
    hinput,
    FSCTL_LOCK_VOLUME,
    NULL,
    0,
    NULL,
    0,
    &byte_count,
    NULL
    ))
  {
    err = GetLastError();
    fprintf(stderr, "Error %u locking input volume.n", err);
    return err;
  }
  if (!DeviceIoControl
    (
    hinput,
    IOCTL_DISK_GET_DRIVE_GEOMETRY,
    NULL,
    0,
    &source_diskgeometry,
    sizeof(source_diskgeometry),
    &byte_count,
    NULL
    ))
  {
    err = GetLastError();
    fprintf(stderr, "Error %u getting device geometry.n", err);
    return err;
  }
  switch (source_diskgeometry.MediaType)
  {
  case Unknown:
  case RemovableMedia:
  case FixedMedia:
    if (!DeviceIoControl
      (
      hinput,
      IOCTL_DISK_GET_LENGTH_INFO,
      NULL,
      0,
      &source_disklength,
      sizeof(source_disklength),
      &byte_count,
      NULL
      ))
    {
      err = GetLastError();
      fprintf(stderr, "Error %u getting input device length.n", err);
      return err;
    }
    fprintf(stderr, "nInput disk has %I64i bytes.n", source_disklength.Length.QuadPart);
    break;
  default:
    source_disklength.Length.QuadPart = 
      source_diskgeometry.Cylinders.QuadPart *
      source_diskgeometry.TracksPerCylinder *
      source_diskgeometry.SectorsPerTrack *
      source_diskgeometry.BytesPerSector;
    fprintf(stderr, 
      "n"
      "Input device appears to be a floppy disk.  WARNING: if this is not an"
      "floppy disk the calculated disk size is probably incorrect, resultingn"
      "in an incomplete copy.n"
      "n"
      "Input disk has %I64i bytes.n",
      source_disklength.Length.QuadPart);
    break;
  }
  houtput = CreateFile
    (
    target_device_name,
    GENERIC_READ | GENERIC_WRITE,
    0,
    NULL,
    OPEN_EXISTING,
    FILE_FLAG_NO_BUFFERING,
    NULL
    );
  if (houtput == INVALID_HANDLE_VALUE) {
    err = GetLastError();
    fprintf(stderr, "Error %u opening output device.n", err);
    return err;
  }
  if (!DeviceIoControl
    (
    houtput,
    FSCTL_LOCK_VOLUME,
    NULL,
    0,
    NULL,
    0,
    &byte_count,
    NULL
    ))
  {
    err = GetLastError();
    fprintf(stderr, "Error %u locking output volume.n", err);
    return err;
  }
  if (!DeviceIoControl
    (
    houtput,
    IOCTL_DISK_GET_DRIVE_GEOMETRY,
    NULL,
    0,
    &target_diskgeometry,
    sizeof(target_diskgeometry),
    &byte_count,
    NULL
    ))
  {
    err = GetLastError();
    fprintf(stderr, "Error %u getting output device geometry.n", err);
    return err;
  }
  switch (target_diskgeometry.MediaType)
  {
  case Unknown:
  case RemovableMedia:
  case FixedMedia:
    if (!DeviceIoControl
      (
      houtput,
      IOCTL_DISK_GET_LENGTH_INFO,
      NULL,
      0,
      &target_disklength,
      sizeof(target_disklength),
      &byte_count,
      NULL
      ))
    {
      err = GetLastError();
      fprintf(stderr, "Error %u getting output device length.n", err);
      return err;
    }
    fprintf(stderr, "Output disk has %I64i bytes.nn", target_disklength.Length.QuadPart);
    break;
  default:
    target_disklength.Length.QuadPart = 
      target_diskgeometry.Cylinders.QuadPart *
      target_diskgeometry.TracksPerCylinder *
      target_diskgeometry.SectorsPerTrack *
      target_diskgeometry.BytesPerSector;
    fprintf(stderr, 
      "n"
      "Output device appears to be a floppy disk.  WARNING: if this is not an"
      "floppy disk the calculated output device size is probably incorrect,n"
      "which might result in an incomplete copy.n"
      "n"
      "Output disk has %I64i bytes.n"
      "n", 
      target_disklength.Length.QuadPart);
    break;
  }
  if (source_disklength.Length.QuadPart == target_disklength.Length.QuadPart)
  {
    transfer_length.QuadPart = source_disklength.Length.QuadPart;
  }
  else if (source_disklength.Length.QuadPart < target_disklength.Length.QuadPart)
  {
    printf("Input shorter than output.  Part of the output disk will not be written to.nn");
    transfer_length.QuadPart = source_disklength.Length.QuadPart;
  }
  else
  {
    printf("Output shorter than input.  Copy will be truncated to output length.nn");
    transfer_length.QuadPart = target_disklength.Length.QuadPart;
  }
  offset.QuadPart = 0;
  overlapped.hEvent = 0;
  for (;;) 
  {
    overlapped.Offset = offset.LowPart;
    overlapped.OffsetHigh = offset.HighPart;
    if (transfer_length.QuadPart - offset.QuadPart < dump_buffersize) 
    {
      bytes_to_transfer = (DWORD)(transfer_length.QuadPart - offset.QuadPart);
      if (bytes_to_transfer == 0) break;
    }
    else
    {
      bytes_to_transfer = dump_buffersize;
    }
    if (!ReadFile(hinput, buffer, bytes_to_transfer, NULL, &overlapped)) 
    {
      err = GetLastError();
      printf("Error %u initiating read from input file.n", err);
      return err;
    }
    if (!GetOverlappedResult(hinput, &overlapped, &byte_count, TRUE)) 
    {
      err = GetLastError();
      printf("Error %u reading from input file.n", err);
      return err;
    }
    if (byte_count != bytes_to_transfer)
    {
      err = GetLastError();
      printf("Internal error - partial read.  Last error code %u.n", err);
      printf("bytes_to_transfer = %u; byte_count = %u.n", bytes_to_transfer, byte_count);
      if (byte_count == 0) return ERROR_INVALID_FUNCTION;
      bytes_to_transfer = byte_count;
    }
    if (!WriteFile(houtput, buffer, bytes_to_transfer, NULL, &overlapped)) 
    {
      err = GetLastError();
      if (err != ERROR_IO_PENDING)
      {
        printf("Error %u initiating write to output disk.n", err);
        return err;
      }
    }
    if (!GetOverlappedResult(houtput, &overlapped, &byte_count, TRUE)) 
    {
      err = GetLastError();
      printf("Error %u writing to output disk.n", err);
      return err;
    }
    if (byte_count != bytes_to_transfer)
    {
      printf("Internal error - partial write.n");
      printf("bytes_to_transfer = %u; byte_count = %u.n", bytes_to_transfer, byte_count);
      return ERROR_INVALID_FUNCTION;
    }
    offset.QuadPart += bytes_to_transfer;
  }
  if (transfer_length.QuadPart == source_disklength.Length.QuadPart)
  {
    overlapped.Offset = offset.LowPart;
    overlapped.OffsetHigh = offset.HighPart;
    if (!ReadFile(hinput, buffer, source_diskgeometry.BytesPerSector, NULL, &overlapped)) 
    {
      err = GetLastError();
      if (err == ERROR_HANDLE_EOF)
      {
        printf("Copy successfully completed.n");      
        return 0;
      }
      printf("Error %u initiating read from input disk past end of file.n", err);
      return err;
    }
    if (!GetOverlappedResult(hinput, &overlapped, &byte_count, TRUE)) 
    {
      err = GetLastError();
      if (err == ERROR_HANDLE_EOF)
      {
        printf("Copy successfully completed.n");
        return 0;
      }
      printf("Error %u reading from input disk past end of file.n", err);
      return err;
    }
    if (byte_count == 0)
    {
      printf("Copy successfully completed.n"); 
      return 0;
    }
    printf("WARNING: the expected amount of data was successfully copied,n"
           "but end of file not detected on input disk.  The copy mightn"
           "not be complete.");
    result = ERROR_MORE_DATA;
    return 0;
  }
  printf("Copy successfully completed.n");
  return 0;
}
int wmain(int argc, wchar_t ** argv)
{
  if (argc < 4)
  {
    printf("Syntax: n"
      "To save an image of a physical drive:n"
      "diskimage /save \\.\PhysicalDrive0 file.imgn"
      "diskimage /save \\.\A: file.imgn"
      "To write from an image file to a physical drive:n"
      "diskimage /write file.img \\.\PhysicalDrive0n"
      "diskimage /write file.img \\.\A:n"
      "To clone input drive 0 to output drive 1:n"
      "diskimage /clone \\.\PhysicalDrive0 \\.\PhysicalDrive1n"
      );
    return 1;
  }
  if (_wcsicmp(argv[1], L"/save") == 0)
  {
    return save(argv[2], argv[3]);
  }
  else if (_wcsicmp(argv[1], L"/write") == 0)
  {
    return write(argv[2], argv[3]);
  }
  else if (_wcsicmp(argv[1], L"/clone") == 0)
  {
    return clone(argv[2], argv[3]);
  }
  else
  {
    printf("Invalid argument.  Use /? for syntax help.n");
    return 1;
  }
}