Device Input and Output Control (IOCTL)

    The DeviceIoControl function provides a device input and output control (IOCTL) interface through which an application can communicate directly with a device driver. The DeviceIoControl function is a general-purpose interface that can send control codes to a variety of devices. Each control code represents an operation for the driver to perform. For example, a control code can ask a device driver to return information about the corresponding device, or direct the driver to carry out an action on the device, such as formatting a disk.

   A number of standard control codes are defined in the SDK header files. In addition, device drivers can define their own device-specific control codes. For a list of standard control codes included in the SDK documentation, see the Remarks section of DeviceIoControl.

   The types of control codes you can specify depend on the device being accessed and the platform on which your application is running. Applications can use the standard control codes or device-specific control codes to perform direct input and output operations on a floppy disk drive, hard disk drive, tape drive, or CD-ROM drive.

   DeviceIoControl函数提供了一个输入/输出控制设备(IOCTL)的接口,通过该接口应用程序可以方便的与设备驱动程序进行通信。DeviceIoControl函数是一个通用接口,通过它可以发送控制命令到各种设备。每一个控制命令都对应驱动程序的一个操作。举例来说:某个控制命令让驱动程序返回相应的设备或者直接命令驱动对设备执行某项操作(如格式化硬盘)。

   在SDK头文件中定义了一些标准的控制命令。此外,设备驱动可以定义特定的设备控制命令。对于包含在SDK文档中的标准控制命令,请参阅使用DeviceIoControl备注部分。
   控制命令类型的制定依赖于当前应用程序所使用的平台及其所使用的设备。应用程序可以直接在硬盘驱动器,磁带驱动器,或CD - ROM驱动器上使用标准的控制命令或特定于设备的控制命令来执行输入和输出操作。

微软官方的一个例子程序:

#define UNICODE 1
#define _UNICODE 1

/* The code of interest is in the subroutine GetDriveGeometry. The 
   code in main shows how to interpret the results of the call. */

#include <windows.h>
#include <winioctl.h>
#include <stdio.h>

BOOL GetDriveGeometry(DISK_GEOMETRY *pdg)
{
  HANDLE hDevice;               // handle to the drive to be examined 
  BOOL bResult;                 // results flag
  DWORD junk;                   // discard results

  hDevice = CreateFile(TEXT("\\\\.\\PhysicalDrive0"),  // drive to open
                    0,                // no access to the drive
                    FILE_SHARE_READ | // share mode
                    FILE_SHARE_WRITE, 
                    NULL,             // default security attributes
                    OPEN_EXISTING,    // disposition
                    0,                // file attributes
                    NULL);            // do not copy file attributes

  if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive
  {
    return (FALSE);
  }

  bResult = DeviceIoControl(hDevice,  // device to be queried
      IOCTL_DISK_GET_DRIVE_GEOMETRY,  // operation to perform
                             NULL, 0, // no input buffer
                            pdg, sizeof(*pdg),     // output buffer
                            &junk,                 // # bytes returned
                            (LPOVERLAPPED) NULL);  // synchronous I/O

  CloseHandle(hDevice);

  return (bResult);
}

int main(int argc, char *argv[])
{
  DISK_GEOMETRY pdg;            // disk drive geometry structure
  BOOL bResult;                 // generic results flag
  ULONGLONG DiskSize;           // size of the drive, in bytes

  bResult = GetDriveGeometry (&pdg);

  if (bResult) 
  {
    printf("Cylinders = %I64d\n", pdg.Cylinders);
    printf("Tracks/cylinder = %ld\n", (ULONG) pdg.TracksPerCylinder);
    printf("Sectors/track = %ld\n", (ULONG) pdg.SectorsPerTrack);
    printf("Bytes/sector = %ld\n", (ULONG) pdg.BytesPerSector);

    DiskSize = pdg.Cylinders.QuadPart * (ULONG)pdg.TracksPerCylinder *
      (ULONG)pdg.SectorsPerTrack * (ULONG)pdg.BytesPerSector;
    printf("Disk size = %I64d (Bytes) = %I64d (Gb)\n", DiskSize,
           DiskSize / (1024 * 1024 * 1024));
  } 
  else 
  {
    printf ("GetDriveGeometry failed. Error %ld.\n", GetLastError ());
  }

  return ((int)bResult);
}
 
另外一个牛人的程序:
NTFS Sparse Files with C#

Sparse files in NTFS has been a pet interest for a while now, but I just never had the combination of time and patience to deal with DeviceIoControl and get a C# wrapper in place. Well, with our offices closed over Eid here in Dubai I had the time and, as a side effect to procrastination, the patience. Plus the xbox I won is delayed until after the weekend, so...

   

代码
36 using (FileStream fs = NTFS.Sparse.SparseFile.Create(filename))

37 {

38 // Create a 640kB file containing 128k blocks of ones and zeros

39 WriteBlocks(fs);

40

41 // TODO: Set sparse range inside the file

42 uint startIx = 128 * 1024;

43 NTFS.Sparse.SparseFile.SetSparseBlock(fs.SafeFileHandle, startIx, 128 * 1024);

44 startIx = 128 * 1024 * 3;

45 NTFS.Sparse.SparseFile.SetSparseBlock(fs.SafeFileHandle, startIx, 128 * 1024);

 

 

This effectively creates a file with content and then after the fact punches two holes in the file by telling NTFS that those two sections are all-zero and do not need to have disk space allocated. When you test, bear in mind you won't get exactly half the file size back, there is still overhead for managing the empty sections. For a good description of sparse files and how to use them (in C++), take a look at this article on FlexHex.com.

The magic happens when you create the file:

 

代码
13 public static FileStream Create(string fileName)

14 {

27

28 uint dwShareMode = (uint)Win32.EFileShare.None;

29 uint dwDesiredAccess = (uint)Win32.EFileAccess.GenericWrite;

30 uint dwFlagsAndAttributes = (uint)Win32.EFileAttributes.Normal;

31 uint dwCreationDisposition = (uint)Win32.ECreationDisposition.CreateAlways;

32

33 SafeFileHandle fileHandle =

34 Win32.Methods.CreateFileW(

35 fileName,

36 dwDesiredAccess,

37 dwShareMode,

38 IntPtr.Zero,

39 dwCreationDisposition,

40 dwFlagsAndAttributes,

41 IntPtr.Zero);

42

43 int bytesReturned = 0;

44 NativeOverlapped lpOverlapped = new NativeOverlapped();

45 bool result =

46 Win32.Methods.DeviceIoControl(

47 fileHandle,

48 Win32.EIoControlCode.FsctlSetSparse,

49 IntPtr.Zero,

50 0,

51 IntPtr.Zero,

52 0,

53 ref bytesReturned,

54 ref lpOverlapped);

55

56 return new System.IO.FileStream(fileHandle, System.IO.FileAccess.Write);

 

 

 

And more magic behind the .SetSparseBlock method which you can call after you determined what parts is / needs to be zero and just directly set it as sparse instead of writing zeros.

  

代码
59 public static void SetSparseBlock(SafeFileHandle fileHandle, uint startIx, uint zeroBlockLength)

60 {

71 int dwTemp = 0;

72 NativeOverlapped lpOverlapped = new NativeOverlapped();

73

74 Win32.FILE_ZERO_DATA_INFORMATION fzd;

75 fzd.FileOffset = startIx;

76 fzd.BeyondFinalZero = startIx + zeroBlockLength;

77

78 IntPtr ptrFZD = IntPtr.Zero;

79 try

80 {

81 ptrFZD = Marshal.AllocHGlobal(Marshal.SizeOf(fzd));

82 Marshal.StructureToPtr(fzd, ptrFZD, true);

83

84 bool result =

85 Win32.Methods.DeviceIoControl(

86 fileHandle,

87 Win32.EIoControlCode.FsctlSetZeroData,

88 ptrFZD,

89 Marshal.SizeOf(fzd),

90 IntPtr.Zero,

91 0,

92 ref dwTemp,

93 ref lpOverlapped);

94 }

95 finally

96 {

97 Marshal.DestroyStructure(ptrFZD, typeof(Win32.FILE_ZERO_DATA_INFORMATION));

98 }

 

 

Lines 78 to 82 with 97 make me really nervous. This is my first attempt at interop, so I can do with some code review here! In fact, the attached project uses "unsafe" code to get the pointer of the struct directly instead of via the Marshal class.

The coolest part about sparse files is that if an application unaware of the sparse nature of the file accesses it, NTFS does the right thing anyway, so you can happily let unaware applications edit the files you create with your sparse aware application.

The last feature worth mentioning is the allocation of sparse space at the end of a file. I hear this is a typical scenario: writing one byte or more far past the end of the current file size. You can accomplish this easily since the file is already marked as sparse and NTFS understands you skipped a few bytes like so:

  

代码
47 // TODO: Set sparse block at the end of the file / extend existing file with zero'd content for length N

48 fs.Seek(8192 * 1024, SeekOrigin.End);

49 fs.WriteByte(1);

50 }

 

 

Line 50 is the closing of an earlier using (FileStream ...

So there you have it: sparse files in .NET with just a little interop gymnastics. But I'm no interop or Win32 guru, so use at your own risk!

FYI: You can use "fsutil" from the commandline to verify this works. Only, one minor thing, when you "queryrange", it actually tells you the ranges of NONzero data. Thought you should know. That only took me an HOUR to figure out!!!

 

 

posted @ 2010-05-26 10:47  xinjun  阅读(613)  评论(0编辑  收藏  举报