Figure 2   CDriverObject

driver.h

 ////////////////////////////////////////////////////////////////////////////////
// Module Name:    driver.h
// Abstract:        CDriverObject Interface
// Environment:    Windows NT 3.51/4.0, MS Visual C++ 4.x
// Notes:
//    CDriverObject encapsulates the DRIVER_OBJECT data structure.
//
//    IMPORTANT:
//        A CDriverObject class object MUST be statically instantiated by the
//        user. Dynamic instantiation is not supported. Each driver object
//        represents the image of a loaded kernel-mode driver. A pointer to
//        the driver object is an input parameter to a driver's DriverEntry
//        and optional Reinitialize routines and to its Unload routine, if
//        any.
//
////////////////////////////////////////////////////////////////////////////////

#ifndef driver_h
#define driver_h

#include "device.h"

class CDriverObject 
{
public:
    // ctor/dtor
    CDriverObject( PCWSTR DriverClassName = NULL );
    virtual ~CDriverObject();

    // Simply returns NULL because dynamic instantiation of an
    // object of this class is not supported.
    PVOID operator new( size_t size );

    // Cast operator for PDRIVER_OBJECT.
    operator PDRIVER_OBJECT();

    VOID SetDriverObject( PDRIVER_OBJECT DriverObject );

    // called from within DriverEntry routine. Must call
    // IoRegisterDriverReinitialization passing this pointer as Context
    // argument. Then must call virtual OnInitialize() member.
    NTSTATUS Initialize( PUNICODE_STRING RegistryPath );

    // called from within DriverUnload() routine. This routine MUST:
    //        1. Call m_DeviceObject->Unload()
    //        2. Call virtual OnUnload()
    static VOID Unload( PDRIVER_OBJECT DriverObject );

    // Driver Entry routine exported to IOManager.
    static NTSTATUS DriverEntry( PDRIVER_OBJECT DriverObject, 
                                 PUNICODE_STRING RegistryPath );

    // Calls IoAssignResources().
    NTSTATUS AssignResources(
        IN PUNICODE_STRING RegistryPath, 
        IN PIO_RESOURCE_REQUIREMENTS_LIST RequestedResources, 
        OUT PCM_RESOURCE_LIST* AllocatedResources );

    NTSTATUS AssignSlotResources(
        IN PUNICODE_STRING RegistryPath, 
        IN INTERFACE_TYPE BusType,  
        IN ULONG BusNumber, 
        IN ULONG SlotNumber, 
        OUT PCM_RESOURCE_LIST* AllocatedResources );

    // calls IoReportResourceUsage().
    NTSTATUS ReportResourceUsage(
        IN PCM_RESOURCE_LIST DriverList, 
        IN ULONG DriverListSize, 
        IN BOOLEAN bOverRideConflict, 
        OUT PBOOLEAN bConflictDetected );

    // set in constructor
    PUNICODE_STRING GetClassName( VOID );

    PUNICODE_STRING GetHardwareDatabase( VOID );

protected:
    // called from Unload() member, gives derived classes an opportunity to
    // cleanup before UnloadDevices is called (e.g. if driver supports a
    // DEVICE_OBJECT that is not a CDeviceObject)
    virtual VOID OnUnload( void );

    // called by Initialize() routine, gives derived classes an opportunity to
    // perform additional initialization (e.g. if driver supports a
    // DEVICE_OBJECT that is not a CDeviceObject)
    virtual NTSTATUS OnInitialize( PUNICODE_STRING RegistryPath );

    // calls IoRegisterDriverReinitialization().
    VOID RegisterDriverReinitialization(
        PDRIVER_REINITIALIZE DriverReinitializationRoutine,
        PVOID Context );

    // createDevices must create a device object during
    // CDriverObject::Initialize(). Following is an example of how a derived
    // class MUST create a CDeviceObject object:
    //        CDeviceObject* CMyDriverObject::CreateDevice()
    //        {
    //            NTSTATUS status;
    //            new( *this, DeviceName, DeviceType, DeviceCharacteristics,
    //                bExclusive, status ) CMyDeviceObject( this );
    //            return status;
    //        }
    virtual NTSTATUS CreateDevices( void ) = 0;

    // setup as StartIo callback for DRIVER_OBJECT, calls OnStartIo()
    static VOID StartIo( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );

    // calls OnReadStartIo(), OnWriteStartIo(), or OnStartIo members of
    // CDeviceObject. Override to perform custom StartIo handling (e.g.
    // if driver supports a DEVICE_OBJECT that is not a CDeviceObject)
    virtual VOID OnStartIo( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );

    // dispatch handler, calls OnIrpMjXxx
    static NTSTATUS IrpMjXxx( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );

    // overridable dispatch handler. Override to perform custom StartIo
    // handling (e.g. if driver supports a DEVICE_OBJECT that is not a
    // CDeviceObject)
    virtual NTSTATUS OnIrpMjXxx( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );

private:
    // methods
    NTSTATUS InitDevices( PUNICODE_STRING RegistryPath );
    VOID UnloadDevices( VOID );

    // members
    PDRIVER_OBJECT m_DriverObject;

    // the only driver object
    static CDriverObject* s_DriverObject;

    //    Set in constructor
    UNICODE_STRING m_ClassName;
};

////////////////////////////////////////////////////////////////////////////////
// inlines
//
inline CDriverObject::operator PDRIVER_OBJECT()
{
    return m_DriverObject;
}

inline VOID CDriverObject::SetDriverObject( IN PDRIVER_OBJECT DriverObject )
{
    ASSERT( NULL == m_DriverObject );
    ASSERT( NULL != DriverObject );

    m_DriverObject = DriverObject;
    return;
}

inline PUNICODE_STRING CDriverObject::GetClassName( void )
{
    return &m_ClassName;
}

inline PUNICODE_STRING CDriverObject::GetHardwareDatabase( void )
{
    return m_DriverObject->HardwareDatabase;
}

#endif driver_h

driver.cpp

 ////////////////////////////////////////////////////////////////////////////////
// Module Name:    driver.cpp
// Abstract:        CDriverObject Implementation
// Environment:    Windows NT 3.51/4.0, MS Visual C++ 2.x
////////////////////////////////////////////////////////////////////////////////

// disable some warnings
#pragma warning( disable: 4699 )    // Note: Creating precompiled header
#pragma warning( disable: 4201 )    // nameless struct/union
#pragma warning( disable: 4514 )    // unreferenced inline function has been removed
#pragma warning( disable: 4705 )    // statement has no effect
#pragma warning( disable: 4711 )    // function 'function' selected for automatic inline expansion

#include <idrivrpp.h>
#include "cppx.h"
#include "driver.h"

#define DEFAULT_CLASS_NAME L"Other"

////////////////////////////////////////////////////////////////////////////////
// static initialization
//
CDriverObject* CDriverObject::s_DriverObject = NULL;

////////////////////////////////////////////////////////////////////////////////
// ctor/dtor
//
CDriverObject::CDriverObject( IN PCWSTR DriverClassName /* = NULL */ )
    :    m_DriverObject( NULL )
{
    // Set the class name for the driver
    //@@@ why not use our CUnicodeString class
    if( NULL != DriverClassName )
    {
        RtlInitUnicodeString( &m_ClassName, DriverClassName );
    }
    else
    {
        RtlInitUnicodeString( &m_ClassName, DEFAULT_CLASS_NAME );
    }

    ASSERT( NULL != s_DriverObject );
    s_DriverObject = this;
    return;
}

CDriverObject::~CDriverObject()
{
    s_DriverObject = NULL;
    return;
}

PVOID CDriverObject::operator new( IN size_t /* size */ )
{
    // Dynamic allocation not supported.
    return NULL;
}

NTSTATUS CDriverObject::DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath )
{
    s_DriverObject->SetDriverObject( DriverObject );
    DriverObject->DriverUnload = Unload;
    DriverObject->DriverStartIo = StartIo;

    // Setup the entry points.
    DriverObject->MajorFunction[ IRP_MJ_CLEANUP ] = IrpMjXxx;
    DriverObject->MajorFunction[ IRP_MJ_CLOSE ] = IrpMjXxx;
    DriverObject->MajorFunction[ IRP_MJ_CREATE ] = IrpMjXxx;
    DriverObject->MajorFunction[ IRP_MJ_DEVICE_CONTROL ] = IrpMjXxx;
    DriverObject->MajorFunction[ IRP_MJ_FLUSH_BUFFERS ] = IrpMjXxx;
    DriverObject->MajorFunction[ IRP_MJ_INTERNAL_DEVICE_CONTROL ] = IrpMjXxx;
    DriverObject->MajorFunction[ IRP_MJ_QUERY_INFORMATION ] = IrpMjXxx;
    DriverObject->MajorFunction[ IRP_MJ_READ ] = IrpMjXxx;
    DriverObject->MajorFunction[ IRP_MJ_SET_INFORMATION ] = IrpMjXxx;
    DriverObject->MajorFunction[ IRP_MJ_SHUTDOWN ] = IrpMjXxx;
    DriverObject->MajorFunction[ IRP_MJ_WRITE ] = IrpMjXxx;

    return s_DriverObject->Initialize( RegistryPath );
}

NTSTATUS CDriverObject::Initialize( IN PUNICODE_STRING RegistryPath )
{
    // create the devices for this driver
    NTSTATUS status;
    if( NT_SUCCESS( status = CreateDevices() ) )
    {
        // If we successfully create all of the devices, finish the
        // initialization of the driver object.
        if( NT_SUCCESS( status = InitDevices( RegistryPath ) ) )
        {
            // Register for shutdown notifications for all devices
            // @@@ don't we need to register for each device?
            IoRegisterShutdownNotification( m_DriverObject->DeviceObject );
            status = OnInitialize( RegistryPath );

            // either by CreateDevices or OnInitialize, there should be at
            // least one device (though not necessarily a CDeviceObject)
            ASSERT( NULL != m_DriverObject->DeviceObject );
        }
        else
        {
            // InitDevices failed... unload the driver
            Unload( m_DriverObject );
        }
    }

    return status;
}

NTSTATUS CDriverObject::OnInitialize( IN PUNICODE_STRING /* RegistryPath */ )
{
    return STATUS_SUCCESS;
}

NTSTATUS CDriverObject::InitDevices( PUNICODE_STRING RegistryPath )
{
    // loop through list of devices (maintained by IO Manager)
    PDEVICE_OBJECT pDeviceObject = m_DriverObject->DeviceObject;
    while( NULL != pDeviceObject )
    {
        // get device object
        CDeviceObject* pDevice = (CDeviceObject*) pDeviceObject->DeviceExtension;
        ASSERT( NULL != pDevice );

        // call the most-derived's Initialize routine
        NTSTATUS status;
        if( ! NT_SUCCESS( (status = pDevice->Initialize( RegistryPath )) ) )
        {
            // Initialize failed... return (CDriverObject::Initialize 
            // will cleanup)
            return status;
        }

        // get the next device
        pDeviceObject = pDeviceObject->NextDevice;
    }
    
    return STATUS_SUCCESS;
}

VOID CDriverObject::Unload( IN PDRIVER_OBJECT /* DriverObject */ )
{
    ASSERT( NULL != s_DriverObject );
    s_DriverObject->OnUnload();
    s_DriverObject->UnloadDevices();

    cppShutdown();
    return;
}

VOID CDriverObject::OnUnload()
{
    return;
}

VOID CDriverObject::UnloadDevices()
{
    // loop through list of devices (maintained by IO Manager)
    PDEVICE_OBJECT pDeviceObject = m_DriverObject->DeviceObject;
    while( NULL != pDeviceObject )
    {
        // get device object
        CDeviceObject* pDevice = 
          (CDeviceObject*) pDeviceObject->DeviceExtension;
        ASSERT( NULL != pDevice );

        // call the most-derived device object's Unload routine
        pDevice->Unload();

        // get the next device (before we delete this one)
        pDeviceObject = pDeviceObject->NextDevice;

        // delete the device object
        delete pDevice;
    }

    return;
}

VOID CDriverObject::StartIo( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
{
    ASSERT( NULL != s_DriverObject );
    s_DriverObject->OnStartIo( DeviceObject, Irp );
    return;
}

VOID CDriverObject::OnStartIo( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
{
    ASSERT_IRQL_EQ( DISPATCH_LEVEL );

    // get device object
    CDeviceObject* pObject = (CDeviceObject*) DeviceObject->DeviceExtension;
    ASSERT( NULL != pObject );

    // get stack location
    PIO_STACK_LOCATION CurrentStackLocation = IoGetCurrentIrpStackLocation( Irp );
    ASSERT( NULL != CurrentStackLocation );

    // dispatch to the CDeviceObject derived handlers
    switch( CurrentStackLocation->MajorFunction )
    {
    case IRP_MJ_READ:
        pObject->OnStartReadIo( Irp );
        break;

    case IRP_MJ_WRITE:
        pObject->OnStartWriteIo( Irp );
        break;

    default:
        pObject->OnStartIo( Irp );
        break;
    }

    return;
}

NTSTATUS CDriverObject::IrpMjXxx(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp )
{
    ASSERT( NULL != s_DriverObject );
    return s_DriverObject->OnIrpMjXxx( DeviceObject, Irp );
}

NTSTATUS CDriverObject::OnIrpMjXxx(
    IN PDEVICE_OBJECT DeviceObject, 
    IN PIRP Irp )
{
    // get device object
    CDeviceObject* pObject = (CDeviceObject*) DeviceObject->DeviceExtension;
    ASSERT( NULL != pObject );

    // get stack location
    PIO_STACK_LOCATION CurrentStackLocation = IoGetCurrentIrpStackLocation(Irp );
    ASSERT( NULL != CurrentStackLocation );

    // dispatch to the CDeviceObject-derived handlers
    // @@@ we could add parameter "crackers" to these calls... (i.e. pass as
    // @@@ parameters the most commonly used members of the IO_STACK_LOCATION)
    switch( CurrentStackLocation->MajorFunction )
    {
    case IRP_MJ_CREATE:
        return pObject->OnIrpMjCreate( Irp );

    case IRP_MJ_CLEANUP:
        return pObject->OnIrpMjCleanup( Irp );

    case IRP_MJ_CLOSE:
        return pObject->OnIrpMjClose( Irp );

    case IRP_MJ_READ:
        return pObject->OnIrpMjRead( Irp );

    case IRP_MJ_WRITE:
        return pObject->OnIrpMjWrite( Irp );

    case IRP_MJ_DEVICE_CONTROL:
        return pObject->OnIrpMjDeviceControl( Irp );

    case IRP_MJ_INTERNAL_DEVICE_CONTROL:
        return pObject->OnIrpMjInternalDeviceControl( Irp );

    case IRP_MJ_QUERY_INFORMATION:
        return pObject->OnIrpMjQueryInformation( Irp );

    case IRP_MJ_SET_INFORMATION:
        return pObject->OnIrpMjSetInformation( Irp );
    case IRP_MJ_FLUSH_BUFFERS:
        return pObject->OnIrpMjFlushBuffers( Irp );

    case IRP_MJ_SHUTDOWN:
        return pObject->OnIrpMjShutdown( Irp );
    }

    TRACE( "CDriverObject::OnIrpMjXxx - received an Irp that wasn't "
           "setup in the dispatch table!" );
    Irp->IoStatus.Information = 0;
    Irp->IoStatus.Status = STATUS_SUCCESS;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
    return STATUS_SUCCESS;
}

NTSTATUS CDriverObject::ReportResourceUsage(
    IN PCM_RESOURCE_LIST DriverList, 
    IN ULONG DriverListSize, 
    IN BOOLEAN OverRideConflict, 
    OUT PBOOLEAN ConflictDetected )
{
    ASSERT( NULL != DriverList );
    ASSERT( NULL != ConflictDetected );

    return IoReportResourceUsage(
        &m_ClassName, 
        m_DriverObject, 
        DriverList, 
        DriverListSize, 
        NULL, 
        NULL, 
        0, 
        OverRideConflict,
        ConflictDetected );
}

NTSTATUS CDriverObject::AssignSlotResources(
    IN PUNICODE_STRING RegistryPath, 
    IN INTERFACE_TYPE BusType,  
    IN ULONG BusNumber, 
    IN ULONG SlotNumber, 
    OUT PCM_RESOURCE_LIST* AllocatedResources )
{
    ASSERT( NULL != RegistryPath );
    ASSERT( NULL != AllocatedResources );

    // If this CDeviceObject is an exclusive device object for this driver, NULL
    // should be passed as the DeviceObject argument to alAssignSlotResources(),
    // so that device-specific registry management is not required. If this is
    // not an exclusive CDeviceObject object, then it is necessary to create and
    // update the registry path passed to the DriverEntry routine in order to
    // maintain device-specific resource data for each set of resources claimed
    // under the driver-specific path contained in the DriverEntry's
    // RegistryPath.
    return HalAssignSlotResources( 
        RegistryPath, 
        &m_ClassName,
        m_DriverObject,
        NULL,
        BusType,
        BusNumber,
        SlotNumber,
        AllocatedResources );
}

NTSTATUS CDriverObject::AssignResources(
    IN PUNICODE_STRING RegistryPath, 
    IN PIO_RESOURCE_REQUIREMENTS_LIST RequestedResources, 
    OUT PCM_RESOURCE_LIST* AllocatedResources )
{
    ASSERT( NULL != RegistryPath );
    ASSERT( NULL != RequestedResources );
    ASSERT( NULL != AllocatedResources );

    return IoAssignResources( 
        RegistryPath, 
        &m_ClassName,
        m_DriverObject,
        NULL,
        RequestedResources,
        AllocatedResources );
}

VOID CDriverObject::RegisterDriverReinitialization(
    IN PDRIVER_REINITIALIZE DriverReinitializationRoutine, 
    IN PVOID Context )
{
    ASSERT_IRQL_EQ( PASSIVE_LEVEL );
    ASSERT( NULL != DriverReinitializationRoutine );    
    IoRegisterDriverReinitialization( *this, DriverReinitializationRoutine,
                                      Context );
    return;
}

extern "C" NTSTATUS DriverEntry(
    IN PDRIVER_OBJECT DriverObject, 
    IN PUNICODE_STRING RegistryPath )
{
    cppInit();

    NTSTATUS status = CDriverObject::DriverEntry( DriverObject, RegistryPath );

    // if DriverEntry does not return success, Unload is not called,
    // so we need to call it for ourselves.
    if( STATUS_SUCCESS != status )
    {
        CDriverObject::Unload( DriverObject );
    }

    return status;
}

Figure 3   CDeviceObject

device.h

 ////////////////////////////////////////////////////////////////////////////////
// Module Name:    device.h
// Abstract:        CDeviceObject Interface
// Environment:    Windows NT 3.51/4.0, MS Visual C++ 4.x
////////////////////////////////////////////////////////////////////////////////

#ifndef device_h
#define device_h 

#include "debug.h"

// forward declarations
class CDriverObject;

// CDeviceObject encapsulates the NTDDK DEVICE_OBJECT structure.
class CDeviceObject 
{
public:
    // ctor/dtor
    CDeviceObject( IN CDriverObject* DriverObject = NULL );
    virtual ~CDeviceObject();

    // This operator new must return NULL.
    PVOID operator new( size_t size );

    // Calls IoCreateDevice() passing pointer to stack PDEVICE_OBJECT and size
    // as DeviceExtensionSize. Upon return from IoCreateDevice(),
    // m_DeviceObject data member will be set by casting
    // pDeviceObject->DeviceExtension to CDeviceObject.
    PVOID operator new(
        IN size_t size, 
        IN PDRIVER_OBJECT DriverObject, 
        IN PUNICODE_STRING DeviceName, 
        IN PUNICODE_STRING Win32DeviceName, 
        IN DEVICE_TYPE DeviceType, 
        IN ULONG DeviceCharacteristics, 
        IN BOOLEAN Exclusive, 
        OUT NTSTATUS& status );

    // Should do nothing, since the IOManager will call IoDeleteDevice() when
    // a driver is unloaded.
    VOID operator delete( IN PVOID p, IN size_t size );

    // Cast operator for PDEVICE_OBJECT.
    operator PDEVICE_OBJECT();

    // called from CDriverObject::Initialize. This call is made from the
    // DriverEntry routine running at IRQL_PASSIVE_LEVEL.
    // IMPORTANT:
    //        This routine MUST create the CInterruptObject and CDpcObject
    //        objects contained by value by derived CDeviceObject class to setup
    //        callbacks to OnInterrupt() and OnDpc(), respectively.
    NTSTATUS Initialize( IN PUNICODE_STRING RegistryPath );

    // Nonvirtual called by CDriverDevice::Unload() when the driver is
    // unloading in order to perform cleanup.
    VOID Unload( VOID );

    CDriverObject* GetDriver( VOID );

    // called by CInterruptObject::SynchronizeExecutionForXXX() to
    // synchronize device I/O programming.
    virtual BOOLEAN OnSynchronizeExecutionForRead( VOID );
    virtual BOOLEAN OnSynchronizeExecutionForWrite( VOID );

    // called by CInterruptObject::OnInterrupt()
    virtual BOOLEAN OnInterrupt( VOID );

    // called from CDpcObject::Dpc()
    virtual VOID OnDpc( IN PIRP Irp, IN PVOID Context );

    // overridable IO handlers
    virtual VOID OnStartIo( IN PIRP Irp );
    virtual VOID OnStartReadIo( IN PIRP Irp );
    virtual VOID OnStartWriteIo( IN PIRP Irp );

    // overridable dispatch handlers
    virtual NTSTATUS OnIrpMjCleanup( IN PIRP Irp );
    virtual NTSTATUS OnIrpMjClose( IN PIRP Irp );
    virtual NTSTATUS OnIrpMjCreate( IN PIRP Irp );
    virtual NTSTATUS OnIrpMjDeviceControl( IN PIRP Irp );
    virtual NTSTATUS OnIrpMjInternalDeviceControl( IN PIRP Irp );
    virtual NTSTATUS OnIrpMjFlushBuffers( IN PIRP Irp );
    virtual NTSTATUS OnIrpMjQueryInformation( IN PIRP Irp );
    virtual NTSTATUS OnIrpMjRead( IN PIRP Irp );
    virtual NTSTATUS OnIrpMjSetInformation( IN PIRP Irp );
    virtual NTSTATUS OnIrpMjShutdown( IN PIRP Irp );
    virtual NTSTATUS OnIrpMjWrite( IN PIRP Irp );

    virtual VOID GetDeviceDescription( OUT PDEVICE_DESCRIPTION DeviceDescription );

protected:
    // called from Unload() member.
    virtual VOID OnUnload( VOID );

    // called by CDeviceObject::Initialize(), which is called from
    // CDriverObject::Initialize() during driver initialization.
    // IMPORTANT:
    //        A class derived from CDeviceObject MUST call
    //        CInterruptObject::Initialize() for its CInterruptObject member,
    //        if it exists.
    virtual NTSTATUS OnInitialize( IN PUNICODE_STRING RegistryPath );

    // called by Cancel().
    virtual VOID OnCancel( IN PIRP Irp );

    // calls IoStartPacket() with itself as the target device object.
    VOID StartPacket( IN PIRP Irp, IN PULONG Key = NULL, 
                      IN BOOLEAN bCancellable = TRUE );

    // calls IoInitializeTimer(). A driver's IoTimer routine is called once per
    // second after the driver enables the timer by calling IoStartTimer. The
    // driver can disable the timer by calling StopTimer and can re-enable it
    // again with StartTimer. Callers of IoInitializeTimer must be running at
    // IRQL PASSIVE_LEVEL. Should be called by derived class object in its
    // OnInitialize() routine.
    NTSTATUS InitializeTimer( IN PIO_TIMER_ROUTINE TimerRoutine, 
                              IN PVOID Context );

    // Calls IoRaiseHardError().
    VOID RaiseHardError( IN PIRP Irp, IN PVPB Vpb );

    // Calls IoRequestDpc().
    VOID RequestDpc( IN PIRP Irp, IN PVOID Context );

    // Calls IoStartNextPacket().
    VOID StartNextPacket( IN BOOLEAN bCancellable = TRUE );

    // Calls IoStartNextPacketByKey().
    VOID StartNextPacket( IN BOOLEAN bCancellable, ULONG Key );

    // Calls IoStartTimer().
    VOID StartTimer( VOID );

    // Calls IoStopTimer().
    VOID StopTimer( VOID );

    // Calls IoUnregisterShutdownNotification().
    VOID UnregisterShutdownNotification( VOID );

    // Calls IoRegisterShutdownNotification().
    NTSTATUS RegisterShutdownNotification( VOID );

    // Calls IoCancelIrp(). IoCancelIrp() is bracketed by
    // IoAquireCancelSpinLock() and IoReleaseCancelSpinLock().
    BOOLEAN CancelIrp( IN PIRP Irp );

    BOOLEAN PciFindDevice(
        IN USHORT VendorId, 
        IN USHORT DeviceId, 
        OUT PULONG BusNumber, 
        OUT PPCI_SLOT_NUMBER PciSlotNumber, 
        OUT PPCI_COMMON_CONFIG PciCommonConfig );

    // Calls HalAssignSlotResources()
    NTSTATUS AssignSlotResources(
        IN PUNICODE_STRING RegistryPath, 
        IN PUNICODE_STRING DriverClassName, 
        IN INTERFACE_TYPE BusType,  
        IN ULONG BusNumber, 
        IN ULONG SlotNumber, 
        OUT PCM_RESOURCE_LIST* AllocatedResources );

    // Calls IoAssignResources().
    NTSTATUS AssignResources(
        IN PUNICODE_STRING RegistryPath, 
        IN PUNICODE_STRING DriverClassName, 
        IN PIO_RESOURCE_REQUIREMENTS_LIST RequestedResources, 
        OUT PCM_RESOURCE_LIST* AllocatedResources );

    NTSTATUS ReportResourceUsage(
        IN PUNICODE_STRING DriverClassName,
        IN PCM_RESOURCE_LIST DeviceList,
        IN ULONG DeviceListSize,
        IN BOOLEAN bOverRideConflict,
        OUT PBOOLEAN bConflictDetected );

private:
    // cancel routine.
    static VOID Cancel( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp );

protected:
    // members
    PDEVICE_OBJECT m_DeviceObject;
    CDriverObject* m_DriverObject;
    UNICODE_STRING m_Win32DeviceName;
    UNICODE_STRING m_RegistryPath;
};

////////////////////////////////////////////////////////////////////////////////
// inlines
//
inline CDriverObject* CDeviceObject::GetDriver( void )
{
    return m_DriverObject;
}

inline NTSTATUS CDeviceObject::InitializeTimer( PIO_TIMER_ROUTINE TimerRoutine,
                                                PVOID Context )
{
    return IoInitializeTimer( *this, TimerRoutine, Context );
}

inline VOID CDeviceObject::StartTimer( void )
{
    IoStartTimer( m_DeviceObject );
    return;
}

inline VOID CDeviceObject::StopTimer( void )
{
    IoStopTimer( m_DeviceObject );
    return;
}

inline VOID CDeviceObject::RaiseHardError( PIRP Irp, PVPB Vpb )
{
    IoRaiseHardError( Irp, Vpb, m_DeviceObject );
    return;
}

inline VOID CDeviceObject::StartPacket(
    PIRP Irp,
    PULONG Key /* = NULL */,
    BOOLEAN bCancellable /* = TRUE*/ )
{
    ASSERT_IRQL_LE( DISPATCH_LEVEL );
    IoStartPacket( m_DeviceObject, Irp, Key, bCancellable ? Cancel : NULL );
    return;
}

inline VOID CDeviceObject::StartNextPacket( BOOLEAN bCancellable /* = TRUE */ )
{
    IoStartNextPacket( m_DeviceObject, bCancellable );
    return;
}

inline VOID CDeviceObject::StartNextPacket( BOOLEAN bCancellable, ULONG Key )
{
    IoStartNextPacketByKey( m_DeviceObject, bCancellable, Key );
    return;
}

inline NTSTATUS CDeviceObject::RegisterShutdownNotification( void )
{
    return IoRegisterShutdownNotification( m_DeviceObject );
}

inline VOID CDeviceObject::UnregisterShutdownNotification( void )
{
    IoUnregisterShutdownNotification( m_DeviceObject );
    return;
}

inline VOID CDeviceObject::GetDeviceDescription( OUT PDEVICE_DESCRIPTION DeviceDescription )
{
    RtlZeroMemory( DeviceDescription, sizeof DEVICE_DESCRIPTION );
    return;
}

inline VOID CDeviceObject::RequestDpc( PIRP Irp, PVOID Context )
{
    IoRequestDpc( m_DeviceObject, Irp, Context );
    return;
}

#endif device_h

device.cpp

 ////////////////////////////////////////////////////////////////////////////////
// Module Name:        device.cpp
// Abstract:            CDeviceObject Implementation
// Environment:        Windows NT 3.51/4.0, MS Visual C++ 4.x
////////////////////////////////////////////////////////////////////////////////

// disable some warnings
#pragma warning( disable: 4699 )    // Note: Creating precompiled header
#pragma warning( disable: 4201 )    // nameless struct/union
#pragma warning( disable: 4514 )    // unreferenced inline function 
                                    // has been removed
#pragma warning( disable: 4355 )    // 'this' : used in base member 
                                    // initializer list
#pragma warning( disable: 4705 )    // Statement has no effect
#pragma warning( disable: 4711 )    // function 'function' selected for
                                    // automatic inline expansion

#include <idrivrpp.h>
#include "driver.h"
#include "device.h"

////////////////////////////////////////////////////////////////////////////////
// ctor/dtor
//
CDeviceObject::CDeviceObject( IN CDriverObject* DriverObject /* = NULL */ )
    :    m_DriverObject( DriverObject )
{
    // initialize structures (do not initialize m_Win32Device - 
    // it is done in new)
    m_RegistryPath.Buffer = NULL;
    m_RegistryPath.Length = 0;
    m_RegistryPath.MaximumLength = 0;
}

CDeviceObject::~CDeviceObject()
{
}

////////////////////////////////////////////////////////////////////////////////
// storage management
//
PVOID CDeviceObject::operator new( size_t /*size*/ )
{
    return NULL;
}

PVOID CDeviceObject::operator new(
    IN size_t size, 
    IN PDRIVER_OBJECT DriverObject, 
    IN PUNICODE_STRING DeviceName, 
    IN PUNICODE_STRING Win32DeviceName, 
    IN DEVICE_TYPE DeviceType, 
    IN ULONG DeviceCharacteristics, 
    IN BOOLEAN Exclusive, 
    OUT NTSTATUS& status )
{
    ASSERT( NULL != DriverObject );
    ASSERT( NULL != DeviceName );
    ASSERT( NULL != Win32DeviceName );

    PDEVICE_OBJECT DeviceObject = NULL;
     CDeviceObject* pThis = NULL;

    // create the device, allocating space for this object
    if( STATUS_SUCCESS ==
        (status = IoCreateDevice(
            DriverObject, 
            size, 
            DeviceName, 
            DeviceType, 
            DeviceCharacteristics, 
            Exclusive, 
            &DeviceObject )) )
    {
        ASSERT( NULL != DeviceObject );
        pThis = (CDeviceObject*) DeviceObject->DeviceExtension;

        // The device extension area will be this object's image.
        // Set up the data member pointing to the DEVICE_OBJECT allocated
        // by the IOManager.
        ASSERT( NULL != pThis );
        pThis->m_DeviceObject = DeviceObject;

        // @@@ do we need to force the client to create this symbolic link?
        if( STATUS_SUCCESS == (status = IoCreateSymbolicLink
                                          ( Win32DeviceName, DeviceName )) )
        {
            // allocate space for m_Win32DeviceName
            // @@@ can we use CUnicodeString here
            pThis->m_Win32DeviceName.Buffer = 
                PWSTR( new( PagedPool ) char[ Win32DeviceName->MaximumLength ] );
            if( NULL != pThis->m_Win32DeviceName.Buffer )
            {
                // set maximum length to allocated length
                pThis->m_Win32DeviceName.MaximumLength = 
                    Win32DeviceName->MaximumLength;

                // copy the string
                RtlCopyUnicodeString( &pThis->m_Win32DeviceName, 
                                      Win32DeviceName );
            }
            else
            {
                TRACE( "CDeviceObject::operator new - Failed to allocate "
                       "space for m_Win32DeviceName!" );
                // cleanup occurs in operator delete()
            }
        }
        else
        {
            TRACE( "CDeviceObject::operator new - Failed on call to "
                   "IoCreateSymbolicLink!" );
            // cleanup occurs in operator delete()
        }
    }
    else
    {
        TRACE( "CDeviceObject::operator new - Failed on call to "
               "IoCreateDevice, returning NULL!" );
    }

    return pThis;
}

VOID CDeviceObject::operator delete( IN PVOID p, IN size_t /*size*/ )
{
    ASSERT( NULL != p );
    CDeviceObject* pDevice = (CDeviceObject*) p;

    // did we assign the device object member
    if( NULL != pDevice->m_DeviceObject )
    {
        // did we allocate and assign the m_Win32DeviceNmae member
        if( NULL != pDevice->m_Win32DeviceName.Buffer )
        {
            ASSERT( 0 != pDevice->m_Win32DeviceName.Length );

            // delete the symbolic link
            IoDeleteSymbolicLink( &pDevice->m_Win32DeviceName );
            delete pDevice->m_Win32DeviceName.Buffer;

            // reset members
            pDevice->m_Win32DeviceName.Buffer = NULL;
            pDevice->m_Win32DeviceName.Length = 0;
            pDevice->m_Win32DeviceName.MaximumLength = 0;
        }

        // Free up the DEVICE_OBJECT
        IoDeleteDevice( pDevice->m_DeviceObject );
    }

    return;
}

CDeviceObject::operator PDEVICE_OBJECT()
{
    return m_DeviceObject;
}

NTSTATUS CDeviceObject::Initialize( PUNICODE_STRING RegistryPath )
{
    // allocate space for m_RegistryPath
    // @@@ can we use CUnicodeString here?
    // @@@ do we really need to allocate MaximumLength... 
    // why not Length + 1, also
    // @@@ aren't MaximumLength and Length in bytes not chars 
    // (i.e. new(...) char[ size ])
    m_RegistryPath.Buffer = 
        new( PagedPool ) WCHAR[ RegistryPath->MaximumLength ];
    if( NULL == m_RegistryPath.Buffer )
    {
        TRACE( "CDeviceObject::Initialize - Failed to allocate buffer "
               "for RegistryPath!" );
        return STATUS_NO_MEMORY;
    }

    m_RegistryPath.MaximumLength = RegistryPath->MaximumLength;
    RtlCopyUnicodeString( &m_RegistryPath, RegistryPath );

    return OnInitialize( RegistryPath );
}

NTSTATUS CDeviceObject::OnInitialize( PUNICODE_STRING /* RegistryPath */ )
{
    return STATUS_SUCCESS;
}

// This object should not be deleted by CDriverObject until after this call
VOID CDeviceObject::Unload()
{
    OnUnload();

    // did we allocate and assign the m_RegistryPath member
    if( NULL != m_RegistryPath.Buffer )
    {
        // free up the memory for the registry path buffer.
        delete m_RegistryPath.Buffer;

        // reset members
        m_RegistryPath.Buffer = NULL;
        m_RegistryPath.Length = 0;
        m_RegistryPath.MaximumLength = 0;
    }

    return;
}

VOID CDeviceObject::OnUnload( void )
{
    return;
}

BOOLEAN CDeviceObject::OnSynchronizeExecutionForRead( void )
{
    return TRUE;
}

BOOLEAN CDeviceObject::OnSynchronizeExecutionForWrite( void )
{
    return TRUE;
}

VOID CDeviceObject::OnStartIo( IN PIRP Irp )
{
    ASSERT_IRQL_EQ( DISPATCH_LEVEL );

    // Complete the request
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
}

VOID CDeviceObject::OnStartReadIo( IN PIRP Irp )
{
    ASSERT_IRQL_EQ( DISPATCH_LEVEL );

    PIO_STACK_LOCATION CurrentStackLocation =
        IoGetCurrentIrpStackLocation( Irp );

    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = CurrentStackLocation->Parameters.Read.Length;
    // @@@ do we want to set Information to 0 and call IoCompleteRequest
}

VOID CDeviceObject::OnStartWriteIo( IN PIRP Irp )
{
    ASSERT_IRQL_EQ( DISPATCH_LEVEL );

    PIO_STACK_LOCATION CurrentStackLocation = IoGetCurrentIrpStackLocation( Irp );

    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = CurrentStackLocation->Parameters.Write.Length;
    // @@@ do we want to set Information to 0 and call IoCompleteRequest
}

NTSTATUS CDeviceObject::OnIrpMjCleanup( IN PIRP Irp )
{
    // Complete the request
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return STATUS_SUCCESS;
}

NTSTATUS CDeviceObject::OnIrpMjClose( IN PIRP Irp )
{
    // Complete the request
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return STATUS_SUCCESS;
}

NTSTATUS CDeviceObject::OnIrpMjCreate( IN PIRP Irp )
{
    // Complete the request
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return STATUS_SUCCESS;
}

NTSTATUS CDeviceObject::OnIrpMjDeviceControl( IN PIRP Irp )
{
    // Complete the request
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return STATUS_SUCCESS;
}

NTSTATUS CDeviceObject::OnIrpMjInternalDeviceControl( IN PIRP Irp )
{
    // Complete the request
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return STATUS_SUCCESS;
}

NTSTATUS CDeviceObject::OnIrpMjFlushBuffers( IN PIRP Irp )
{
    // Complete the request
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return STATUS_SUCCESS;
}

NTSTATUS CDeviceObject::OnIrpMjQueryInformation( IN PIRP Irp )
{
    // Complete the request
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return STATUS_SUCCESS;
}

NTSTATUS CDeviceObject::OnIrpMjRead( IN PIRP Irp )
{
    // Mark the request pending
    IoMarkIrpPending( Irp );
    StartPacket( Irp, NULL );

    return STATUS_PENDING;
}

NTSTATUS CDeviceObject::OnIrpMjSetInformation( IN PIRP Irp )
{
    // Complete the request
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return STATUS_SUCCESS;
}

NTSTATUS CDeviceObject::OnIrpMjShutdown( IN PIRP Irp )
{
    // Complete the request
    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );

    return STATUS_SUCCESS;
}

NTSTATUS CDeviceObject::OnIrpMjWrite( IN PIRP Irp )
{
    // Mark the request pending
    IoMarkIrpPending( Irp );
    StartPacket( Irp, NULL );

    return STATUS_PENDING;
}

BOOLEAN CDeviceObject::OnInterrupt( VOID )
{
    return FALSE;
}

VOID CDeviceObject::OnDpc( IN PIRP /* Irp */, IN PVOID /* Context */ )
{
    return;
}

BOOLEAN CDeviceObject::CancelIrp( IN PIRP Irp )
{
    KIRQL Irql;
    IoAcquireCancelSpinLock( &Irql );

    BOOLEAN bResult = IoCancelIrp( Irp );

    IoReleaseCancelSpinLock( Irql );

    return bResult;
}

VOID CDeviceObject::Cancel( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp )
{
    CDeviceObject* pDevice = (CDeviceObject*) DeviceObject->DeviceExtension;
    ASSERT( NULL != pDevice );

    pDevice->OnCancel( Irp );
    return;
}

VOID CDeviceObject::OnCancel( IN PIRP Irp )
{
    Irp->IoStatus.Status = STATUS_CANCELLED;
    Irp->IoStatus.Information = 0;
    IoCompleteRequest( Irp, IO_NO_INCREMENT );
    return;
}

BOOLEAN CDeviceObject::PciFindDevice(
    IN USHORT VendorId, 
    IN USHORT DeviceId, 
    OUT PULONG BusNumber, 
    OUT PPCI_SLOT_NUMBER PciSlotNumber, 
    OUT PPCI_COMMON_CONFIG PciCommonConfig )
{
    // initialize parameters
    *BusNumber = 0;
    PciSlotNumber->u.AsULONG = 0;

    BOOLEAN bFound = FALSE;
    while( FALSE == bFound )
    {
        // get bus data
        ULONG ulReturn = HalGetBusData( 
            PCIConfiguration, 
            *BusNumber, 
            PciSlotNumber->u.AsULONG, 
            PciCommonConfig, 
            sizeof PCI_COMMON_CONFIG );
        
        // pci bus does not exist (assume that these start at 
        // 0 and go up with no gaps)
        if( 0 == ulReturn )
        {
            break;
        }
        else if( sizeof PCI_COMMON_CONFIG == ulReturn )
        {
            // found a card... see if it is ours
            if( VendorId == PciCommonConfig->VendorID && 
                DeviceId == PciCommonConfig->DeviceID )
            {
                // found our card... get its information
                bFound = TRUE;
                break;
            }
        }
        
        //@@@ it appears that only the DeviceNumber of the SlotNumber is used... 
        //@@@ maybe if you have two or more of the same card it is used
        //@@@ if it is never used or if it should be stepped first we could
        //@@@ simplify the code 
        
        // step our BusNumber and/or SlotNumber
        if( 31 == PciSlotNumber->u.bits.DeviceNumber && 
             7 == PciSlotNumber->u.bits.FunctionNumber )
        {
            // try the next bus
            *BusNumber++;
            PciSlotNumber->u.AsULONG = 0;
        }
        else if( 7 == PciSlotNumber->u.bits.FunctionNumber )
        {
            // try the next device number
            PciSlotNumber->u.bits.DeviceNumber++;
            PciSlotNumber->u.bits.FunctionNumber = 0;
        }
        else
        {
            // try the next function number
            PciSlotNumber->u.bits.FunctionNumber++;
        }
    }

    #ifdef _DEBUG
    //@@@ did we find a device, validate its size
    if( bFound )
    {
        ASSERT( sizeof( PCI_COMMON_CONFIG ) ==
            HalSetBusData(
                PCIConfiguration,
                *BusNumber,
                PciSlotNumber->u.AsULONG,
                PciCommonConfig,
                sizeof PCI_COMMON_CONFIG ) );
    }
    #endif
    
    return bFound;
}

NTSTATUS CDeviceObject::AssignSlotResources(
    IN PUNICODE_STRING RegistryPath, 
    IN PUNICODE_STRING DriverClassName, 
    IN INTERFACE_TYPE BusType,  
    IN ULONG BusNumber, 
    IN ULONG SlotNumber, 
    OUT PCM_RESOURCE_LIST* AllocatedResources )
{
    // If this CDeviceObject is an exclusive device object for this driver,
    // NULL should be passed as the DeviceObject argument to
    // HalAssignSlotResources(), so that device-specific registry
    // management is not required. If this is not an exclusive 
    // CDeviceObject object, then it is necessary to create and update the
    // registry path passed to the DriverEntry routine in order
    // to maintain device-specific resource data for each set of
    // resources claimed under the driver-
    // specific path contained in the DriverEntry's RegistryPath.
    return HalAssignSlotResources( 
        RegistryPath, 
        DriverClassName,
        (PDRIVER_OBJECT) *m_DriverObject,
        m_DeviceObject,
        BusType,
        BusNumber,
        SlotNumber,
        AllocatedResources );
}

NTSTATUS CDeviceObject::AssignResources(
    IN PUNICODE_STRING RegistryPath, 
    IN PUNICODE_STRING DriverClassName, 
    IN PIO_RESOURCE_REQUIREMENTS_LIST RequestedResources, 
    OUT PCM_RESOURCE_LIST* AllocatedResources )
{
    return IoAssignResources( 
        RegistryPath, 
        DriverClassName,
        (PDRIVER_OBJECT) *m_DriverObject,
        m_DeviceObject,
        RequestedResources,
        AllocatedResources );
}

NTSTATUS CDeviceObject::ReportResourceUsage(
    IN PUNICODE_STRING DriverClassName,
    IN PCM_RESOURCE_LIST DeviceList,
    IN ULONG DeviceListSize,
    IN BOOLEAN bOverRideConflict,
    OUT PBOOLEAN bConflictDetected )
{
    return IoReportResourceUsage(
        DriverClassName,
        (PDRIVER_OBJECT) *m_DriverObject,
        NULL,
        0,
        m_DeviceObject,
        DeviceList,
        DeviceListSize,
        bOverRideConflict,
        bConflictDetected );
}

Figure 4   Commonly Used Framework Classes

C++ Class

Cardinality

Associated DDK Data Structures

CDpcObject

0-1

None-simply registers DPC routine and gets CDeviceObject pointer from DeviceExtension of PDEVICE_OBJECT argument passed into DPC routine and invokes its OnDpc member function

CSpinLock

0..n

KSPIN_LOCK, KIRQL

CRegistryKey

0..n

HANDLE, PUNICODE_STRING

CUnicodeString

0..n

UNICODE_STRING

CInterruptObject

0-1

KINTERRUPT

CAdapterObject

0..n

ADAPTER_OBJECT

CCustomDpcObject

0..n

KDPC

CDeviceQueue

0..n

KDEVICE_QUEUE

Figure 5   Miscellaneous Framework Objects

C++ Class

Cardinality

Associated DDK Data Structures

CFile

0..n

HANDLE

CTimeOut

0..n

None-uses KeQuerySystemTime to implement a passive timer object

CObject

0(abstract)

HANDLE, PVOID pointing to system object body returned by ObReferenceObjectByHandle

CNamedObject

0(abstract)

UNICODE_STRING for object name. CNamedObject is a descendant of CObject CRegistryKey is an example of an instantiable CNamedObject class type

CFileObject

0(abstract)

None of its own-CFileObject is a descendant of CNamedObject and is really just a more general and less complete implementation of CFile

CLargeInteger

0..n

LARGE_INTEGER

CThread

0..n

KTHREAD

CDList

0..n

PLIST_ENTRY for doubly linked list

CUpperDeviceObject

0..n

Two PDEVICE_OBJECTS: one for the object CUpperDeviceObject is attached to, and one that it owns by virtue of the fact that CUpperDeviceObject is derived from CDeviceObject

Figure 6   CUpperDeviceObject

 ////////////////////////////////////////////////////////////////////////////////
// Module Name:    uldevice.h
// Abstract:        CUpperDeviceObject Interface
// Environment:    Windows NT 3.51/4.0, MS Visual C++ 4.1
// Notes:
//        CDeviceObject which attaches to a lower-level device. The destructor
//        must call IoDetachDevice() in order to decrement the reference count
//        for the target device object.
////////////////////////////////////////////////////////////////////////////////

#ifndef uldevice_h
#define uldevice_h

#include "device.h"

class CUpperDeviceObject : public CDeviceObject 
{
public:
    CUpperDeviceObject( IN CDriverObject* DriverObject );
    virtual ~CUpperDeviceObject();

protected:
    // attachDevice attaches the caller's device object to a named target
    // device object, so that I/O requests bound for the target device are
    // routed first to the caller.
    NTSTATUS AttachDeviceObject( IN PUNICODE_STRING TargetDeviceName );
    NTSTATUS AttachDeviceObject( IN PDEVICE_OBJECT TargetDeviceObject );

    // detachDeviceObject releases an attachment between the caller's device
    // object and a lower driver's device object.
    VOID DetachDeviceObject( VOID );

    // calls IoBuildDeviceIoControlRequest() passing m_AttachedDevice as
    // target DEVICE_OBJECT.
    PIRP BuildDeviceIoControlRequest(
          IN ULONG IoControlCode, 
          IN PVOID InputBuffer, 
          IN ULONG InputBufferLength, 
          IN PVOID OutputBuffer, 
          IN ULONG OutputBufferLength, 
          IN BOOLEAN bInternalDeviceIoControl, 
          IN PKEVENT Event, 
          OUT PIO_STATUS_BLOCK StatusBlock );

    // IoCallDriver returns the NTSTATUS value that a lower driver set in the
    // IO status block for the given request or STATUS_PENDING if the request
    // was queued for additional processing.
    // Comments:
    //        IoCallDriver assigns the DeviceObject input parameter to the device
    //        object field of the IRP stack location for the next lower driver.
    //        An IRP passed in a call to IoCallDriver becomes inaccessible to the
    //        higher-level driver, unless the higher-level driver has set up its
    //        IoCompletion routine for the IRP with IoSetCompletionRoutine. If it
    //        does, the IRP input to the driver-supplied IoCompletion routine has
    //        its I/O status block set by the lower driver(s) and all lower-level
    //        driver(s)' I/O stack locations filled with zeros.
    NTSTATUS CallDriver( IN PIRP Irp );

    // calls IoSetHardErrorOrVerifyDevice().
    // Passes m_AttachedDeviceObject as target.
    VOID SetHardErrorOrVerifyDevice( IN PIRP Irp );

    CCHAR GetAttachedDeviceStackSize( VOID );

private:
    // lower-level driver device object that is obtained in call to Attach().
    PDEVICE_OBJECT m_AttachedDeviceObject;
};

////////////////////////////////////////////////////////////////////////////////
// inlines
//
inline NTSTATUS CUpperDeviceObject::CallDriver( IN PIRP Irp )
{
    ASSERT_IRQL_LE( DISPATCH_LEVEL );
    return IoCallDriver( m_AttachedDeviceObject, Irp );
}

inline VOID CUpperDeviceObject::SetHardErrorOrVerifyDevice( IN PIRP Irp )
{
    ASSERT_IRQL_LE( DISPATCH_LEVEL );
    IoSetHardErrorOrVerifyDevice( Irp, m_AttachedDeviceObject );
    return;
}

inline CCHAR CUpperDeviceObject::GetAttachedDeviceStackSize( void )
{
    return (NULL != m_AttachedDeviceObject) ?
        m_AttachedDeviceObject->StackSize : 0;
}

#endif uldevice_h

Figure 7   Header Files

 #include "dpc.h"        // CDpcOjbect
#include "mem.h"        // globlal new and delete
#include "intrpt.h"     // CInterruptObject
#include "uldevice.h"   // CUpperDeviceObject
#include "driver.h"     // CDriverObject
#include "device.h"     // CDeviceObject
#include "adapter.h"    // CAdapterObject
#include "bertdev.h"    // CBertDevice
#include "dlist.h"      // CDListEntry, CDList
#include "unicode.h"    // CUnicodeString
#include "custdpc.h"    // CCustomDpcObject
#include "linteger.h"   // CLargeInteger
#include "file.h"       // CFile
#include "timeout.h"    // CTimeOut
#include "object.h"     // CObject
#include "thread.h"     // CThread
#include "devqueue.h"   // CDeviceQueueEntry, CDeviceQueue
#include "spinlock.h"   // CSpinLock
#include "regkey.h"     // CRegistryKey

Figure 8   sampdev.cpp

 //    ******************************************************
//    *  Module Name:    sampdev.cpp
//    *  Abstract:    SampleDevice Device object class implementation
//    *  Environment:
//    *
//    *  Notes:
//    * <Assumptions>
//    * <Other of interest>
//    *
//    ******************************************************
#pragma warning (disable : 4201 )    // nonstandard extension used :
                                     // nameless struct/union 
#pragma warning (disable : 4514 )    // unreferenced inline function has
                                     // been removed

#include "sampdev.h"
#include "debug.h"

///////////////////////////////////////////////////////////////////////////////
// ctor/dtor
///////////////////////////////////////////////////////////////////////////////
CSampleDevice::CSampleDevice():
                             CPciDevice( NULL ),
                             m_InputDmaSize( 0  ),
                             m_OutputDmaSize( 0 )
{
}

CSampleDevice::CSampleDevice( IN CDriverObject *DriverObject ) :
                              CPciDevice( DriverObject ),
                              m_InputDmaSize( 256 ),
                              m_OutputDmaSize( 256 )
{
}

CSampleDevice::~CSampleDevice()
{
}

NTSTATUS CSampleDevice::OnInitialize( IN PUNICODE_STRING RegistryPath )
{
    NTSTATUS    status = CPciDevice::OnInitialize( RegistryPath );

    if( status != STATUS_SUCCESS )
        return status;

    // Initialize the adapter objects
    // Each gets a page-size, cache-enabled common buffer
    DEVICE_DESCRIPTION    DeviceDescription;
    
    GetDeviceDescription( &DeviceDescription );
        
    TRACE("OnInitialize");

    if( STATUS_SUCCESS != 
        (status = m_ReadAdapter.Initialize( 
                        RegistryPath,            // RegistryPath
                        m_InputDmaSize,          // Size of common buffer
                        0x1000,                  // Alignment
                        FALSE,                   // Cache enabled common buffer
                        &DeviceDescription )) )  // Device description
    {
        TRACE("OnInitialize Failed to initialize read adapter!");
        return status;
    }
    ASSERT( 0L == m_ReadAdapter.GetCommonBufferLogicalAddress().HighPart );

    if( STATUS_SUCCESS != 
        (status = m_WriteAdapter.Initialize( 
                        RegistryPath,           // RegistryPath
                        m_OutputDmaSize,        // Size of common buffer
                        0x1000,                 // Alignment
                        FALSE,                  // Cache enabled common buffer
                        &DeviceDescription )) ) // Device description
    {
        TRACE("OnInitialize Failed to initialize write adapter!");
        return status;
    }
    ASSERT( 0L == m_WriteAdapter.GetCommonBufferLogicalAddress().HighPart );
    TRACE("Read Common Buffer Phys = %X, Virt = %X", 
          m_ReadAdapter.GetCommonBufferLogicalAddress().LowPart,
          m_ReadAdapter.GetCommonBuffer() );
    TRACE("Write Common Buffer Phys = %X, Virt = %X", 
          m_WriteAdapter.GetCommonBufferLogicalAddress().LowPart,
          m_WriteAdapter.GetCommonBuffer() );

    // Setup the custom DPCs
    m_DpcForWriteIsr.InitializeDpcRequest( DpcForWriteIsr, this );
    m_DpcForReadIsr.InitializeDpcRequest( DpcForReadIsr, this );

    return STATUS_SUCCESS;
}

NTSTATUS CSampleDevice::OnIrpMjDeviceControl( IN PIRP Irp )
{
    TRACE("OnIrpMjDeviceControl");
    PIO_STACK_LOCATION request = IoGetCurrentIrpStackLocation( Irp );
    NTSTATUS Status = STATUS_INVALID_PARAMETER;
    ULONG Information = 0;

    CompleteRequest( Irp, Status, Information ); 
    return Status;
}

NTSTATUS CSampleDevice::OnIrpMjRead( IN PIRP Irp )
{
    TRACE("OnIrpMjRead");

    NTSTATUS Status = STATUS_PENDING;

    // If we're not busy, start the IRP
    if( !m_ReadQueue.InsertData( Irp ) )
    {
        // Only one outstanding write request supported
        if( NULL != m_ReadRequest.Irp )
        {
            TRACE("OnIrpMjRead - Invalid Device Request!!!");
            Status = STATUS_INVALID_DEVICE_REQUEST;
        }
        else
        {
            PIO_STACK_LOCATION request = IoGetCurrentIrpStackLocation( Irp );

            // Cache the Irp
            m_ReadRequest.SpinLock.Acquire();
            m_ReadRequest.Irp = Irp;
            m_ReadRequest.Buffer = MmGetSystemAddressForMdl( Irp->MdlAddress );
            m_ReadRequest.Length = request->Parameters.Read.Length;
            m_ReadRequest.SpinLock.Release();

            // Synchronize device manipulation for reading from device
            m_Interrupt.SynchronizeExecutionForRead();
        }
    }

    if( STATUS_PENDING != Status )
        CompleteRequest( Irp, Status );
    else
        IoMarkIrpPending( Irp );

    return Status;
}

NTSTATUS CSampleDevice::OnIrpMjWrite( IN PIRP Irp )
{
    TRACE("OnIrpMjWrite");

    NTSTATUS Status = STATUS_PENDING;

    // If we're not busy, start the IRP
    if( !m_WriteQueue.InsertData( Irp ) )
    {
        // Only one outstanding write request supported
        if( NULL != m_WriteRequest.Irp )
        {
            TRACE("OnIrpMjWrite - Invalid Device Request!!!");
            Status = STATUS_INVALID_DEVICE_REQUEST;
        }
        else
        {
            PIO_STACK_LOCATION request = IoGetCurrentIrpStackLocation( Irp );

            // Cache the Irp
            m_WriteRequest.SpinLock.Acquire();
            m_WriteRequest.Irp = Irp;
            m_WriteRequest.Buffer = MmGetSystemAddressForMdl( Irp->MdlAddress );
            m_WriteRequest.Length = request->Parameters.Write.Length;
            m_WriteRequest.SpinLock.Release();

            // Synchronize device manipulation for writing to device
            m_Interrupt.SynchronizeExecutionForWrite();
        }
    }

    if( STATUS_PENDING != Status )
        CompleteRequest( Irp, Status );
    else
        IoMarkIrpPending( Irp );

    return Status;
}

VOID CSampleDevice::OnUnload()
{
    TRACE("OnUnload");

    // Don't EVER forget to call CAdapterObject::Unload()
    m_ReadAdapter.Unload();
    m_WriteAdapter.Unload();
    DisconnectInterrupt();
    CPciDevice::OnUnload();
}

BOOLEAN CSampleDevice::OnInterrupt()
{
    TRACE("OnInterrupt");
    NTSTATUS Status = STATUS_SUCCESS;

//    if( !OurInterrupt )
//        return FALSE;

    // Has a write happened?        
//    if( IsWriteInterrupt )
//    {    
        // Queue DPC for write ISR
        if( !m_DpcForWriteIsr.InsertQueue( (PVOID)Status ) )
        {
            TRACE( "OnInterrupt - Failed to queue Write DPC!" );
        }
//    }

    // Has a read happened?        
//    if( IsReadInterrupt )
//    {    
        // Queue DPC for read ISR
        if( !m_DpcForReadIsr.InsertQueue( (PVOID)Status ) )
        {
            TRACE( "OnInterrupt - Failed to queue Read DPC!" );
        }
//    }

    TRACE("Leaving OnInterrupt");
    return TRUE;
}

VOID CSampleDevice::GetDeviceDescription( OUT PDEVICE_DESCRIPTION DeviceDescription )
{
    DeviceDescription->Version              = DEVICE_DESCRIPTION_VERSION;
    DeviceDescription->Master               = TRUE,          // Busmaster Device
    DeviceDescription->ScatterGather        = FALSE,         // No scatter/
                                                             // gather
    DeviceDescription->DemandMode           = FALSE,         // Don't use system
                                                             // DMA controller
    DeviceDescription->AutoInitialize       = FALSE,         // Don't use system
                                                             // DMA controller
    DeviceDescription->Dma32BitAddresses    = TRUE,          // 32 bit addresses
    DeviceDescription->IgnoreCount          = FALSE,         // IgnoreCount
    DeviceDescription->Reserved1            = FALSE,         // Reserved1
    DeviceDescription->Reserved2            = FALSE,         // Reserved2
    DeviceDescription->BusNumber            = m_ulBusNumber, // BusNumber
    DeviceDescription->DmaChannel           = 0,             // No system DMA
                                                             // channel involved
    DeviceDescription->InterfaceType        = PCIBus,        // BusType
    DeviceDescription->DmaWidth             = Width32Bits,   // 32-bit wide Dma
    DeviceDescription->DmaSpeed             = MaximumDmaSpeed,
    DeviceDescription->MaximumLength        = 8196,          // MaximumLength
    DeviceDescription->DmaPort              = 0;             // DmaPort
}

BOOLEAN CSampleDevice::OnSynchronizeExecutionForRead()
{
    // This is where you setup the device for a read from the device
    return TRUE;
}

BOOLEAN CSampleDevice::OnSynchronizeExecutionForWrite()
{
    // This is where you setup the device for a write to the device
    return TRUE;
}

VOID CSampleDevice::OnDpcForWriteIsr( IN NTSTATUS Status )
{
    TRACE("Entering OnDpcForWriteIsr");
    ASSERT( NULL != m_WriteRequest.Irp );

    // Get the next IRP in the queue
    PIRP NextIrp = PIRP(m_WriteQueue.RemoveData());
    PIRP CurrentIrp;
    ULONG Information;

    // Update the IO Request
    m_WriteRequest.SpinLock.AcquireAtDpcLevel();
    CurrentIrp = m_WriteRequest.Irp;
    Information = STATUS_SUCCESS == Status ? m_WriteRequest.Length : 0;
    m_WriteRequest.Irp = NextIrp;
    m_WriteRequest.SpinLock.ReleaseFromDpcLevel();

    // Complete the current WriteIrp
    CompleteRequest(CurrentIrp, Status, Information );

    if( NextIrp )
    {
        PIO_STACK_LOCATION request = IoGetCurrentIrpStackLocation( NextIrp );
        m_WriteRequest.Buffer = MmGetSystemAddressForMdl( NextIrp->MdlAddress );
        m_WriteRequest.Length = request->Parameters.Write.Length;

        // This is where you'd perform the setup for the next write 
        // to the device
    }

    TRACE("Leaving OnDpcForWriteIsr");
}

VOID CSampleDevice::OnDpcForReadIsr( IN NTSTATUS Status )
{
    TRACE("Entering OnDpcForReadIsr");
    ASSERT( NULL != m_ReadRequest.Irp );
    ASSERT( NULL != m_ReadRequest.Buffer );

    if( STATUS_SUCCESS == Status )
    {
        // Copy the data from the device read buffer to the user
        // buffer.
        RtlMoveMemory( m_ReadRequest.Buffer, 
                       m_ReadAdapter.GetCommonBuffer(), 
                       m_ReadRequest.Length );
    }

    // Get the next IRP in the queue
    PIRP NextIrp = PIRP(m_ReadQueue.RemoveData());
    PIRP CurrentIrp;
    ULONG Information;

    // Update the IO Request
    m_ReadRequest.SpinLock.AcquireAtDpcLevel();
    CurrentIrp = m_ReadRequest.Irp;
    Information = STATUS_SUCCESS == Status ? m_ReadRequest.Length : 0;
    m_ReadRequest.Irp = NextIrp;
    m_ReadRequest.SpinLock.ReleaseFromDpcLevel();

    // Complete the request
    CompleteRequest(CurrentIrp, Status, Information );

    if( NextIrp )
    {
        PIO_STACK_LOCATION request = IoGetCurrentIrpStackLocation( NextIrp );
        m_ReadRequest.Buffer = MmGetSystemAddressForMdl( NextIrp->MdlAddress );
        m_ReadRequest.Length = request->Parameters.Read.Length;

        // This is where you'd perform the setup for 
        // the next read from the device
    }

    TRACE("Leaving OnDpcForReadIsr");
}

BOOLEAN CSampleDevice::OnErrorInterrupt()
{
    TRACE( "OnErrorInterrupt Entered" );

    // Queue up DPCs to complete the outstanding Irps
    if( NULL != m_WriteRequest.Irp )
        m_DpcForWriteIsr.InsertQueue( (PVOID)STATUS_DEVICE_DATA_ERROR );
    if( NULL != m_ReadRequest.Irp )
        m_DpcForReadIsr.InsertQueue( (PVOID)STATUS_DEVICE_DATA_ERROR );

    return TRUE;
}

///////////////////////////////////////
//    Class statics
///////////////////////////////////////
/* static */
VOID CSampleDevice::DpcForWriteIsr(
    IN PKDPC /* Dpc */,
    IN PVOID DeferredContext,
    IN PVOID Status,
    IN PVOID /* SystemArgument2 */ )
{
    ((CSampleDevice*)DeferredContext)->OnDpcForWriteIsr( (NTSTATUS)Status );
}

/* static */
VOID CSampleDevice::DpcForReadIsr(
    IN PKDPC /* Dpc */,
    IN PVOID DeferredContext,
    IN PVOID Status,
    IN PVOID /* SystemArgument2 */)
{
    ((CSampleDevice*)DeferredContext)->OnDpcForReadIsr( (NTSTATUS)Status );
}

///////////////////////////////////////
//    Utility functions
///////////////////////////////////////
VOID CompleteRequest( 
            IN PIRP Irp,
            IN NTSTATUS Status,
            IN ULONG Information /* = 0 */,
            IN CCHAR PriorityBoost /* = IO_NO_INCREMENT */ )
{
    TRACE( "CompletRequest Entered" );
    Irp->IoStatus.Status = Status;
    Irp->IoStatus.Information = Information;
    IoCompleteRequest( Irp, PriorityBoost );
}

sampdev.h

 //    ******************************************************
//    *  Module Name:    sampdev.h
//    *  Abstract:    CSampleDevice Device object class declaration
//    *  Environment:
//    * 
//    *  Notes:
//    * <Assumptions>
//    * <Other of interest>
//    *
//    ******************************************************
#ifndef SAMPDEV_H
#define SAMPDEV_H

#include <idrivrpp.h>
#include "pcidev.h"

//    ****************************
//    *    Predeclarations
//    ****************************

//    ****************************
//    *    Types
//    ****************************
struct CIoRequest
{
    // ctor/dtor    
    CIoRequest();

    PIRP        Irp;
    PVOID        Buffer;
    ULONG        Length;
    CSpinLock    SpinLock;
};
//*****************************************************************
//*    class CIoRequest Inlines
//*****************************************************************
inline CIoRequest::CIoRequest():
                              Irp( NULL ),
                              Buffer( NULL ),
                              Length( Length )
{
}

#define PIO_REQUEST CIoRequest*

//    ****************************
//    *    Macros
//    ****************************
#define min( a, b ) (ULONG)a < (ULONG)b ? a : b

//    ****************************
//    *    Class: CSampleDevice
//    ****************************
class CSampleDevice : public CPciDevice
{
    public:
        CSampleDevice( IN CDriverObject *DriverObject );

        virtual ~CSampleDevice();

    protected:
        CSampleDevice();
        CSampleDevice( IN const CSampleDevice &right );

        //    Virtual from CDeviceObject
        virtual NTSTATUS OnInitialize( IN PUNICODE_STRING RegistryPath );

        //    Virtual from CDeviceObject
        virtual NTSTATUS OnIrpMjDeviceControl( IN PIRP Irp );
        virtual NTSTATUS OnIrpMjRead( IN PIRP Irp );
        virtual NTSTATUS OnIrpMjWrite( IN PIRP Irp );
        virtual VOID OnUnload(VOID);
        virtual BOOLEAN OnInterrupt(VOID);
        virtual VOID GetDeviceDescription
                       (OUT PDEVICE_DESCRIPTION DeviceDescription );

        // called by CInterruptObject::SynchronizeExecutionForXXX() to
        // synchronize device I/O programming.
        virtual BOOLEAN OnSynchronizeExecutionForRead( VOID );
        virtual BOOLEAN OnSynchronizeExecutionForWrite( VOID );

    #ifdef NOTDEF
        virtual NTSTATUS OnIrpMjCreate( IN PIRP Irp );
        virtual NTSTATUS OnIrpMjClose( IN PIRP Irp );
        virtual NTSTATUS OnIrpMjCleanup( IN PIRP Irp );
        virtual NTSTATUS OnIrpMjFlushBuffers( IN PIRP Irp );
        virtual NTSTATUS OnIrpMjInternalDeviceControl( IN PIRP Irp );
        virtual NTSTATUS OnIrpMjQueryInformation( IN PIRP Irp );
        virtual NTSTATUS OnIrpMjSetInformation( IN PIRP Irp );
        virtual VOID OnCancel( IN PIRP Irp );
    #endif

/ The Dpc handlers
        VOID OnDpcForWriteIsr( IN NTSTATUS Status );
        VOID OnDpcForReadIsr( IN NTSTATUS Status );

        // The error interrupt handler
        BOOLEAN OnErrorInterrupt(VOID);

        ////////////////////////////
        // The custom DPC routines
        ////////////////////////////
        static VOID DpcForWriteIsr(
                        IN PKDPC Dpc,
                        IN PVOID DeferredContext,
                        IN PVOID Status,
                        IN PVOID SystemArgument2 );

        static VOID DpcForReadIsr(
                        IN PKDPC Dpc,
                        IN PVOID DeferredContext,
                        IN PVOID Status,
                        IN PVOID SystemArgument2 );

    private:
        CIoRequest          m_ReadRequest;
        CIoRequest          m_WriteRequest;
        CAdapterObject      m_ReadAdapter;
        CAdapterObject      m_WriteAdapter;
        CDeviceQueue        m_ReadQueue;
        CDeviceQueue        m_WriteQueue;
        ULONG               m_InputDmaSize;
        ULONG               m_OutputDmaSize;

        // The custom DPCs for setting up for device reads and writes
        CCustomDpcObject    m_DpcForWriteIsr;
        CCustomDpcObject    m_DpcForReadIsr;
};

//*****************************************************************
//*    C routine prototypes
//*****************************************************************
VOID CompleteRequest( 
            IN PIRP Irp,
            IN NTSTATUS Status,
            IN ULONG Information = 0,
            IN CCHAR PriorityBoost = IO_NO_INCREMENT);

#endif

sampdvr.h

 //    ******************************************************
//    *  Module Name:    sampdrv.h
//    *  Abstract:    CSample DriverObject class declaration
//    *  Environment:
//    *
//    *  Notes:
//    * <Assumptions>
//    * <Other of interest>
//    *
//    ******************************************************
#ifndef SAMPDRV_H
#define SAMPDRV_H

#include <idrivrpp.h>

class SampleDriver : public CDriverObject 
{
  public:
      SampleDriver();

      virtual ~SampleDriver();

  protected:
      SampleDriver(const SampleDriver &right);

      //    Virtual from CDriverObject
      virtual NTSTATUS OnInitialize(PUNICODE_STRING RegistryPath);

      //    Virtual from CDriverObject
      virtual VOID OnUnload();

      //    Pure virtual from CDriverObject to create a derived
      //    CDeviceObject object.
      virtual NTSTATUS CreateDevices(VOID);
};

#endif

sampdvr.cpp

 //    ******************************************************
//    *  Module Name:    sampdrv.cpp
//    *  Abstract:    CSampleDriver class implementation
//    *  Environment:
//    *
//    *  Notes:
//    * <Assumptions>
//    * <Other of interest>
//    *
//    ******************************************************
#pragma warning (disable : 4201 )
#pragma warning (disable : 4514 )
#pragma warning (disable : 4699 )

#include "sampdev.h"

// driver
#include "sampdrv.h"
#include "debug.h"

#define DEVICE_NAME L"\\Device\\SAMPLE"
#define WIN32DEVICE_NAME L"\\DosDevices\\SAMPLE"
#define DRIVER_CLASSNAME L"AudioAccelerator"

SampleDriver g_Driver;
#ifdef _DEBUG
    char szDebugName[] = "SampleDevice";
#endif

// Class SampleDriver 
SampleDriver::SampleDriver()
    : CDriverObject( DRIVER_CLASSNAME )
{
}

SampleDriver::~SampleDriver()
{
}

NTSTATUS SampleDriver::OnInitialize(PUNICODE_STRING RegistryPath)
{
    TRACE("SampleDriver::OnInitialize");

    return CDriverObject::OnInitialize( RegistryPath );
}

VOID SampleDriver::OnUnload()
{
    TRACE("SampleDriver::OnUnLoad");
    CDriverObject::OnUnload();
}

NTSTATUS SampleDriver::CreateDevices()
{
    UNICODE_STRING    DeviceName,
                    Win32DeviceName;
    NTSTATUS        status;

    TRACE("SampleDriver::CreateDevice entered");

    RtlInitUnicodeString( &DeviceName, DEVICE_NAME );
    RtlInitUnicodeString( &Win32DeviceName, WIN32DEVICE_NAME );

    // Call new for each CDeviceObject that needs to be instantiated.
    new( *this, 
        &DeviceName,
        &Win32DeviceName,
        FILE_DEVICE_UNKNOWN,
        0,
        FALSE,
        status ) CSampleDevice(this);

    return status;
}