Article ID: 120170 - View products that this article applies to.
This article was previously published under Q120170
Each time CreateFile() or OpenFile() is called to open a device, volume, directory, or data file, the I/O manager creates a file object. A handle to the file object is returned to the caller if the call succeeds. Subsequent I/O calls using the handle are considered operations on the same file object. Among the dispatch routines that a kernel-mode driver can supply, there are a few related to the creation and destruction of the file objects. This article discusses the use of the file object from the driver's perspective and the related dispatch routines.
A pointer to the file object is passed to the driver in the current I/O stack location of the IRP, at IrpSp->FileObject. Because I/O requests with the same handle have the same file object, a driver can use the file-object pointer to identify the I/O operations that belong to one open instantiation of a device or file.
NOTE: A driver should not use the value stored in Irp->Tail.Overlay.OriginalFileObject. This field is owned by the I/O manager. It may not contain the valid file object pointer intended for the driver.
Like other objects in Windows NT, each file object header has an open handle count and a reference count. The open handle count represents the number of handles that have been opened to the file object. The reference count represents the number of pointers to the object that are being used in the system. For example, when a file object is first created and a handle is returned to the caller of CreateFile, the file object's open handle count is set to 1. Each time the handle is duplicated or inherited, the open handle count is incremented. Each CloseHandle() call decrements the count. On the other hand, each time the handle is used to send an I/O request, the I/O manager calls the object manager to increment the file object's reference count. The reference count is decremented when each operation is completed. Drivers may also reference or derefence fileobjects by calling Ob(De)referenceObject.
IRP_MJ_CREATEThe dispatch routine for IRP_MJ_CREATE is called when a file object associated with the device is created. This is typically because of a call to CreateFile(), ZwCreateFile(), or indirectly by a higher-level dri er attaching itself by calling IoAttachDevice(). A driver is required to supply a dispatch routine for IRP_MJ_CREATE. Please see the Windows NT DDK documentation for details.
IRP_MJ_CLEANUPThe dispatch routine for IRP_MJ_CLEANUP is called when the last handle (in the whole system) to a file object is closed. In other words, the open handle count for the file object goes to 0. A driver that holds pending IRPs internally must implement a routine for IRP_MJ_CLEANUP. When the routine is called, the driver should cancel all the pending IRPs that belong to the file object identified by the IRP_MJ_CLEAN call. In other words, it should cancel all the IRPs that have the same file-object pointer as the one supplied in the current I/O stack location of the IRP for the IRP_MJ_CLEANUP call. Of course, IRPs belonging to other file objects should not be canceled. Also, if an outstanding IRP is completed immediately, the driver does not have to cancel it.
NOTE: The routine for IRP_MJ_CLEANUP and the cancel routines for individual IRPs serve different purposes. A driver with long-term IRPs should implement both. A cancel routine is called when the system attempts to cancel a specific IRP. This can happen without an IRP_MJ_CLEANUP call. For example, a user-mode thread gets an file handle from another thread and issues I/O requests with the handle. If the thread terminates with some requests pending, the system tries to cancel the pending IRPs that belong to the terminating thread. In this case, the cancel routines for these IRPs are called; but no IRP_MJ_CLEANUP request is issued. Conversely, IRP_MJ_CLEANUP can be issued with no cancel routines being called. As explained earlier, this happens when the last handle to a file object is closed. In this case, even if there are pending IRPs associated with the file object, the system does not try to cancel them. The driver handles the IRPs appropriately in the IRP_MJ_CLEANUP routine.
IRP_MJ_CLOSEThe IRP_MJ_CLOSE dispatch routine is called when a file object opened on the driver is being removed from the system; that is, all file object handles have been closed and the reference count of the file object is down to 0. Certain types of drivers do not need to handle IRP_MJ_CLOSE, mainly drivers of devices that must be available for the system to continue running. In general, this is the place that a driver should "undo" whatever has been done in the routine for IRP_MJ_CREATE.
As discussed earlier, the reference count is different from the open handle count. The reference count can be a non-zero value when the open handle count goes to 0. For example, if a user-mode program closes its last handle to a file object while there are still I/O requests outstanding, the related driver's IRP_MJ_CLEANUP routine is called because the open handle reaches 0, but the IRP_MJ_CLOSE routine is not called because there are still pointer references to the file object being used. The close routine is not called until all of the outstanding I/O requests are completed. Apparently, if the driver performs as planned in the IRP_MJ_CLEANUP routine, as discussed earlier, the IRP_MJ_CLOSE routine is called at the right time.
Article ID: 120170 - Last Review: February 12, 2007 - Revision: 3.2