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 tothe file object is returned to the caller if the call succeeds. SubsequentI/O calls using the handle are considered operations on the same fileobject. Among the dispatch routines that a kernel-mode driver can supply,there are a few related to the creation and destruction of the fileobjects. This article discusses the use of the file object from thedriver's perspective and the related dispatch routines.
A pointer to the file object is passed to the driver in the current I/Ostack location of the IRP, at IrpSp->FileObject. Because I/O requests withthe same handle have the same file object, a driver can use the file-objectpointer to identify the I/O operations that belong to one openinstantiation of a device or file.
NOTE: A driver should not use the value stored inIrp->Tail.Overlay.OriginalFileObject. This field is owned by the I/Omanager. It may not contain the valid file object pointer intended for thedriver.
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.
The dispatch routine for IRP_MJ_CREATE is called when a file objectassociated with the device is created. This is typically because of a call to CreateFile(), ZwCreateFile(), or indirectly by a higher-level drier attaching itself by calling IoAttachDevice(). A driver is required to supply a dispatch routine for IRP_MJ_CREATE. Please see the Windows NT DDKdocumentation for details.
The dispatch routine for IRP_MJ_CLEANUP is called when the last handle (inthe whole system) to a file object is closed. In other words, the openhandle count for the file object goes to 0. A driver that holds pendingIRPs internally must implement a routine for IRP_MJ_CLEANUP. When theroutine is called, the driver should cancel all the pending IRPs thatbelong to the file object identified by the IRP_MJ_CLEAN call. In otherwords, it should cancel all the IRPs that have the same file-object pointeras the one supplied in the current I/O stack location of the IRP for theIRP_MJ_CLEANUP call. Of course, IRPs belonging to other file objects shouldnot be canceled. Also, if an outstanding IRP is completed immediately, thedriver does not have to cancel it.
NOTE: The routine for IRP_MJ_CLEANUP and the cancel routines for individualIRPs serve different purposes. A driver with long-term IRPs shouldimplement both. A cancel routine is called when the system attempts tocancel a specific IRP. This can happen without an IRP_MJ_CLEANUP call. Forexample, a user-mode thread gets an file handle from another thread andissues I/O requests with the handle. If the thread terminates with somerequests pending, the system tries to cancel the pending IRPs that belongto the terminating thread. In this case, the cancel routines for these IRPsare called; but no IRP_MJ_CLEANUP request is issued. Conversely,IRP_MJ_CLEANUP can be issued with no cancel routines being called. Asexplained earlier, this happens when the last handle to a file object isclosed. In this case, even if there are pending IRPs associated with thefile object, the system does not try to cancel them. The driver handles theIRPs appropriately in the IRP_MJ_CLEANUP routine.
The IRP_MJ_CLOSE dispatch routine is called when a file object opened onthe driver is being removed from the system; that is, all file objecthandles have been closed and the reference count of the file object is downto 0. Certain types of drivers do not need to handle IRP_MJ_CLOSE, mainlydrivers of devices that must be available for the system to continuerunning. In general, this is the place that a driver should "undo" whateverhas been done in the routine for IRP_MJ_CREATE.
As discussed earlier, the reference count is different from the open handlecount. The reference count can be a non-zero value when the open handlecount goes to 0. For example, if a user-mode program closes its last handleto a file object while there are still I/O requests outstanding, therelated driver's IRP_MJ_CLEANUP routine is called because the open handlereaches 0, but the IRP_MJ_CLOSE routine is not called because there arestill pointer references to the file object being used. The close routineis not called until all of the outstanding I/O requests are completed.Apparently, if the driver performs as planned in the IRP_MJ_CLEANUProutine, as discussed earlier, the IRP_MJ_CLOSE routine is called at theright time.