Figure 2    Query.c

/*
    Query.c - 1998 James M. Finnegan - Microsoft Systems Journal

    This console app displays all of the device names present in a Windows
    NT system, and tests each for accessibility.
*/
#include <windows.h>
#include <stdio.h>

void main()
{
    static char szDevices[65535];
    char *szCurrentDevice;
    char szName[80];

    int i=0;

    printf("Win32® Device Name Enumerator - James M. Finnegan, 1998\n");
    printf("Copyright (c)1998 Microsoft Systems Journal, All Rights Reserved\n\n");
    
    
    // Get all the current device names...
    if(QueryDosDevice(NULL, szDevices, 65535) != 0)
    {
        printf("%-20.20s  Accessible?\n", "Device Name");
        printf("--------------------  -----------\n");

        for(;;)
        {
            static HANDLE hDriver;

            // Try to open each device name...
            szCurrentDevice = &szDevices[i];

            sprintf(szName,"\\\\.\\%s", szCurrentDevice);

            hDriver = CreateFile(szName,
                             GENERIC_READ | GENERIC_WRITE, 
                             FILE_SHARE_READ | FILE_SHARE_WRITE,
                             0,                     // Default security
                             OPEN_EXISTING,
                             0,  
                             0);                    // No template
        
            // If the open failed, print out the error code
            if(hDriver == INVALID_HANDLE_VALUE)
                printf("%-20.20s  No: Error %ld\n", szCurrentDevice,  
                       GetLastError());
            // Otherwise, print success and close the driver
            else
            {
                printf("%-20.20s  Yes\n", szCurrentDevice);
                // Close the driver
                CloseHandle(hDriver);
            }

            // Go to next NULL character
            while(szDevices[i] != 0)
                i++;

            // Bump pointer to the next string
            i++;

            // The list is double-NULL terminated, so if the character is
            // now NULL, we're at the end
            if(szDevices[i] == 0)
                break;
        }

    }
    else
        printf("No Devices in list!\n");
}

Figure 5    Sample Source in the DDK

ATDISK Driver for ATA hard disk support
FLOPPY Floppy disk driver
DISKPERF Disk filter driver to measure performance
SCSI* Various SCSI support components (disk, tape, CD-ROM)
ATAPI SCSI Port to ATAPI (IDE) miniport interface


Figure 9   Restricted Device Types

FILE_DEVICE_CD_ROM_FILE_SYSTEM
FILE_DEVICE_DISK
FILE_DEVICE_DISK_FILE_SYSTEM
FILE_DEVICE_FILE_SYSTEM
FILE_DEVICE_NETWORK
FILE_DEVICE_NETWORK_FILE_SYSTEM
FILE_DEVICE_TAPE_FILE_SYSTEM
FILE_DEVICE_VIRTUAL_DISK


Figure 10   Accessible Device Types

FILE_DEVICE_BEEP
FILE_DEVICE_CD_ROM
FILE_DEVICE_CONTROLLER
FILE_DEVICE_DATALINK
FILE_DEVICE_DFS
FILE_DEVICE_INPORT_PORT
FILE_DEVICE_KEYBOARD
FILE_DEVICE_MAILSLOT
FILE_DEVICE_MIDI_IN
FILE_DEVICE_MIDI_OUT
FILE_DEVICE_MOUSE
FILE_DEVICE_MULTI_UNC_PROVIDER
FILE_DEVICE_NAMED_PIPE
FILE_DEVICE_NETWORK_BROWSER
FILE_DEVICE_NULL
FILE_DEVICE_PARALLEL_PORT
FILE_DEVICE_PHYSICAL_NETCARD
FILE_DEVICE_PRINTER
FILE_DEVICE_SCANNER
FILE_DEVICE_SERIAL_MOUSE_PORT
FILE_DEVICE_SERIAL_PORT
FILE_DEVICE_SCREEN
FILE_DEVICE_SOUND
FILE_DEVICE_STREAMS
FILE_DEVICE_TAPE
FILE_DEVICE_TRANSPORT
FILE_DEVICE_UNKNOWN
FILE_DEVICE_VIDEO
FILE_DEVICE_WAVE_IN
FILE_DEVICE_WAVE_OUT
FILE_DEVICE_8042_PORT
FILE_DEVICE_NETWORK_REDIRECTOR
FILE_DEVICE_BATTERY
FILE_DEVICE_BUS_EXTENDER
FILE_DEVICE_MODEM
FILE_DEVICE_VDM


Figure 11   Major IOCTL Messages

IRP_MJ_CREATE
IRP_MJ_CREATE_NAMED_PIPE
IRP_MJ_CLOSE
IRP_MJ_READ
IRP_MJ_WRITE
IRP_MJ_QUERY_INFORMATION
IRP_MJ_SET_INFORMATION
IRP_MJ_QUERY_EA
IRP_MJ_SET_EA
IRP_MJ_FLUSH_BUFFERS
IRP_MJ_QUERY_VOLUME_INFORMATION
IRP_MJ_SET_VOLUME_INFORMATION
IRP_MJ_DIRECTORY_CONTROL
IRP_MJ_FILE_SYSTEM_CONTROL
IRP_MJ_DEVICE_CONTROL
IRP_MJ_INTERNAL_DEVICE_CONTROL
IRP_MJ_SHUTDOWN
IRP_MJ_LOCK_CONTROL
IRP_MJ_CLEANUP
IRP_MJ_CREATE_MAILSLOT
IRP_MJ_QUERY_SECURITY
IRP_MJ_SET_SECURITY
IRP_MJ_QUERY_POWER
IRP_MJ_SET_POWER
IRP_MJ_DEVICE_CHANGE
IRP_MJ_QUERY_QUOTA
IRP_MJ_SET_QUOTA
IRP_MJ_PNP_POWER


Figure 12   MSJDrvr.c


 /* MSJDrvr.C - 1998 James M. Finnegan - Microsoft Systems Journal
 This module implements all of the IOCTLs described in the accompanying article
 */
 
 #include "ntddk.h"
 
 #define FILE_DEVICE_UNKNOWN             0x00000022
 #define IOCTL_UNKNOWN_BASE              FILE_DEVICE_UNKNOWN
 
 #define IOCTL_MSJDRVR_GET_STRING     CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0800, 
 METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
 #define IOCTL_MSJDRVR_READ_MBR       CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0801,
 METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
 #define IOCTL_MSJDRVR_READ_CMOS      CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0802,
 METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
 
 void MSJUnloadDriver(PDRIVER_OBJECT DriverObject);
 
 NTSTATUS MSJDispatchCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 
 NTSTATUS MSJDispatchClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 
 NTSTATUS MSJDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
 
 NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
                      IN PUNICODE_STRING RegistryPath)
 /*++
 Routine Description:
 This routine is called when the driver is loaded by NT.
 
 Arguments:
     DriverObject - Pointer to driver object created by system.
     RegistryPath - Pointer to the name of the services node for this driver.
 
 Return Value:
     The function value is the final status from the initialization operation.
 --*/
 {
     NTSTATUS        ntStatus;
     UNICODE_STRING  uszDriverString;
     UNICODE_STRING  uszDeviceString;
     PDEVICE_OBJECT  pDeviceObject;
 
     // Point uszDriverString at the driver name
     RtlInitUnicodeString(&uszDriverString, L"\\Device\\MSJDrvr");
 
     // Create and initialize device object
     ntStatus = IoCreateDevice(DriverObject,
                               0,
                               &uszDriverString,
                               FILE_DEVICE_UNKNOWN,
                               0,
                               FALSE,
                               &pDeviceObject);
 
     if(ntStatus != STATUS_SUCCESS)
         return ntStatus;
 
     // Point uszDeviceString at the device name
     RtlInitUnicodeString(&uszDeviceString, L"\\DosDevices\\MSJDrvr");
 
     // Create symbolic link to the user-visible name
     ntStatus = IoCreateSymbolicLink(&uszDeviceString, &uszDriverString);
 
     if(ntStatus != STATUS_SUCCESS)
     {
         // Delete device object if not successful
         IoDeleteDevice(pDeviceObject);
         return ntStatus;
     }
 
     // Load structure to point to IRP handlers...
     DriverObject->DriverUnload                         = MSJUnloadDriver;
     DriverObject->MajorFunction[IRP_MJ_CREATE]         = MSJDispatchCreate;
     DriverObject->MajorFunction[IRP_MJ_CLOSE]          = MSJDispatchClose;
     DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MSJDispatchIoctl;
     // Return success
     return ntStatus;
 }
 
 NTSTATUS MSJDispatchCreate(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
 {
     Irp->IoStatus.Status = STATUS_SUCCESS;
     Irp->IoStatus.Information=0;
 
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
     return(STATUS_SUCCESS);
 }
 
 NTSTATUS MSJDispatchClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
 {
     Irp->IoStatus.Status = STATUS_SUCCESS;
     Irp->IoStatus.Information=0;
 
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
     return(STATUS_SUCCESS);
 }
 
 typedef struct tagCMOSMemory
 {
     ULONG ulBaseMem;
     ULONG ulExtMem;
 }CMOS_MEMORY, *PCMOS_MEMORY;
 
 NTSTATUS MSJReadDriveZeroMasterBootRecord(PUCHAR pBuffer)
 {
     NTSTATUS        ntStatus;
     UNICODE_STRING  uszDeviceName;
     PFILE_OBJECT    fileObject;
     PDEVICE_OBJECT  pDriveDeviceObject;
 
     // Initialize unicode string
     RtlInitUnicodeString(&uszDeviceName, L"\\DosDevices\\PhysicalDrive0");
 
     // Get a pointer to PhysicalDrive0
     ntStatus = IoGetDeviceObjectPointer(&uszDeviceName,
    
     // If the device object pointer is valid...
     if(NT_SUCCESS(ntStatus))
     {
         IO_STATUS_BLOCK    ioStatus; 
         KEVENT             event; 
         PIRP               pIrp;
 
         LARGE_INTEGER   sectorNum;
 
         KeInitializeEvent(&event, NotificationEvent, FALSE); 
             
         sectorNum.LowPart = sectorNum.HighPart = 0;
 
         pIrp = IoBuildSynchronousFsdRequest(IRP_MJ_READ,
                                             pDriveDeviceObject,
                                             pBuffer,
                                             512,
                                             &sectorNum,
                                             &event,
                                             &ioStatus);
 
         if(!pIrp)
            return FALSE; 
 
         ntStatus = IoCallDriver(pDriveDeviceObject, pIrp);
  
         if(ntStatus == STATUS_PENDING)
         {
             KeWaitForSingleObject(&event, Suspended, KernelMode, FALSE, NULL);
             ntStatus = ioStatus.Status;
         } 
 
         if(!NT_SUCCESS(ntStatus))
             return ntStatus; 
 
         // Ditch the fileObject pointer created earlier
         ObDereferenceObject(fileObject);
     }
     
     return ntStatus;
 }
 
 NTSTATUS MSJReadMemoryStatsFromCMOS(PCMOS_MEMORY pMemory)
 {
     UCHAR byHiByte, byLowByte;
     UCHAR byBuffer[256];
 
 #if 0
     /*
     The following code demonstrates a straight port from old MS-DOS or 16-bit
     Windows code to obtain CMOS data.  It shows that I/O ports can be freely
     poked and prodded via kernel-mode.  Even though this code works, the
     proper way to obtain CMOS info under Windows NT is demonstrated below,
     via the call to HalGetBusData().
     */
     
     // Get base memory bytes from CMOS
     WRITE_PORT_UCHAR((PUCHAR)0x70, 0x15);
     byLowByte = READ_PORT_UCHAR((PUCHAR)0x71);
     WRITE_PORT_UCHAR((PUCHAR)0x70, 0x16);
     byHiByte = READ_PORT_UCHAR((PUCHAR)0x71);
 
     pMemory->ulBaseMem = (byHiByte << 8) | byLowByte;
 
     // Get extended memory bytes from CMOS
     WRITE_PORT_UCHAR((PUCHAR)0x70, 0x17);
     byLowByte = READ_PORT_UCHAR((PUCHAR)0x71);
     WRITE_PORT_UCHAR((PUCHAR)0x70, 0x18);
     byHiByte = READ_PORT_UCHAR((PUCHAR)0x71);
 
     pMemory->ulExtMem = (byHiByte << 8) | byLowByte;
 #endif
 
     // Read the CMOS info from HAL, since it "owns" the CMOS resource...
     HalGetBusData(Cmos, 0, 0, &byBuffer, sizeof(byBuffer));
 
     // Get the values from the appropriate offsets and load up the
     // caller's structure...
     
     // Base memory
     byLowByte = byBuffer[0x15];
     byHiByte = byBuffer[0x16];
     pMemory->ulBaseMem = (byHiByte << 8) | byLowByte;
 
     // Extended memory
     byLowByte = byBuffer[0x17];
     byHiByte = byBuffer[0x18];
     pMemory->ulExtMem = (byHiByte << 8) | byLowByte;
 
     return STATUS_SUCCESS;
 }
 
 NTSTATUS MSJDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
 {
     NTSTATUS ntStatus;
     PIO_STACK_LOCATION     irpStack = IoGetCurrentIrpStackLocation(Irp);
 
         switch(irpStack->Parameters.DeviceIoControl.IoControlCode)
     {
         case IOCTL_MSJDRVR_GET_STRING:
           strcpy(Irp->AssociatedIrp.SystemBuffer, "Hello from NT Kernel-land!\n");
           ntStatus = STATUS_SUCCESS;
           break;
 
         case IOCTL_MSJDRVR_READ_MBR:
            // Read the MBR if the output buffer is at least the size of one sector
            if(irpStack->Parameters.DeviceIoControl.OutputBufferLength >= 512)
              ntStatus = MSJReadDriveZeroMasterBootRecord(
                                                 Irp->AssociatedIrp.SystemBuffer);
             else
                 ntStatus = STATUS_BUFFER_TOO_SMALL;
             break;
 
         case IOCTL_MSJDRVR_READ_CMOS:
             // Query the CMOS if the output buffer can hold our results...
             if(irpStack->Parameters.DeviceIoControl.OutputBufferLength >= 
                 sizeof(CMOS_MEMORY))
                 ntStatus = MSJReadMemoryStatsFromCMOS(Irp-> 
                                                       AssociatedIrp.SystemBuffer);
             else
                 ntStatus = STATUS_BUFFER_TOO_SMALL;
             break;
 
         default:
             break;
     }
 
     Irp->IoStatus.Status = ntStatus;
     
     // Set # of bytes to copy back to user-mode...
     if(ntStatus == STATUS_SUCCESS)
         Irp->IoStatus.Information=irpStack->  
            Parameters.DeviceIoControl.OutputBufferLength;
     else
         Irp->IoStatus.Information = 0;
 
     IoCompleteRequest(Irp, IO_NO_INCREMENT);
     return ntStatus;
 }
 
 void MSJUnloadDriver(PDRIVER_OBJECT DriverObject)
 {
     UNICODE_STRING  uszDeviceString;
 
     IoDeleteDevice(DriverObject->DeviceObject);
 
     RtlInitUnicodeString(&uszDeviceString, L"\\DosDevices\\MSJDrvr");
     IoDeleteSymbolicLink(&uszDeviceString);
 }

Figure 14   Start Values

0 SERVICE_BOOT_START Drivers with this startup type are started by the Windows NT loader very early in the boot process. You should probably avoid this type unless you really need to be loaded early in the boot process.
1 SERVICE_SYSTEM_START The driver will be started during initialization of Windows NT.
2 SERVICE_AUTO_START The driver will be automatically started during system startup (after the GUI is loaded, but before anyone logs in).
3 SERVICE_DEMAND_START The driver can be started on demand (for example, by the Drivers applet in the Control Panel, or by the NET START command at the command prompt, or by an application via the SCM API).
4 SERVICE_DISABLED The driver is disabled and cannot be manipulated (started or stopped).


Figure 15   Error Control Values

0 SERVICE_ERROR_IGNORE The error is logged.
1 SERVICE_ERROR_NORMAL The SCM pop up a dialog box to indicate driver failure to the user.
2 SERVICE_ERROR_SEVERE If the last-known-good configuration is being started, startup continues. Otherwise, the system is restarted with the last-known-good configuration.
3 SERVICE_ERROR_CRITICAL If the last-known-good configuration is being started, startup fails. Otherwise, the system is restarted with the last-known good configuration.


Figure 16   Dynamic.c


 /*
     Dynamic.c - 1998 James M. Finnegan - Microsoft Systems Journal
 
     This module implements the functionality to dynamically load and
     unload via the Service Control Manager.
 */
 #include <windows.h>
 
 BOOL InstallAndStartDriver(SC_HANDLE hSCManager, LPCTSTR DriverName,
                            LPCTSTR ServiceExe)
 {
     SC_HANDLE  hService;
     BOOL       bReturn;
 
 
     // Create the driver entry in the SC Manager.
     hService = CreateService(hSCManager,            // SCManager database
                              DriverName,            // name of service
                              DriverName,            // name to display
                              SERVICE_ALL_ACCESS,    // desired access
                              SERVICE_KERNEL_DRIVER, // service type
                              SERVICE_DEMAND_START,  // start type
                              SERVICE_ERROR_NORMAL,  // error control type
                              ServiceExe,            // service's binary
                              NULL,                  // no load ordering group
                              NULL,                  // no tag identifier
                              NULL,                  // no dependencies
                              NULL,                  // LocalSystem account
                              NULL                   // no password
                              );
 
     // This may fail because the service entry already exists.  However,
     // we've tried to load it earlier, so something clearly is wrong if
     // we can't create it!
     if(!hService)
         return FALSE;
 
     // Start the driver!
     bReturn = StartService(hService, 0, NULL);
 
     // Dispose of the handle...
     CloseServiceHandle(hService);
 
     // Return whatever the driver start returned...
     return bReturn;
 }
 
 BOOL LoadDynamicNTDriver()
 {
     SC_HANDLE   hSCManager;
     char        currentDirectory[128];
     BOOL        bReturn;
 
 
     // Open Service Control Manager on the local machine...
     hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
 
     // Assume driver is in the same directory as the DLL.  Hence, create
     // a fully qualified pathname for the SCM to add to the registry.
     GetCurrentDirectory(128, currentDirectory);
     lstrcat(currentDirectory,"\\MSJDrvr.sys");
     
     // Install drive in the registry and start it...
     bReturn = InstallAndStartDriver(hSCManager, "MSJDrvr", currentDirectory);
 
     // Close the Service Control Manager...
     CloseServiceHandle(hSCManager);
 
     return bReturn;
 }
 
 ////////////////////////////////////////////////////////////////////////////
 //  Unload routines
 ////////////////////////////////////////////////////////////////////////////
 
 BOOL UnloadDynamicNTDriver()
 {
     SC_HANDLE       hSCManager;
     SC_HANDLE       hService;
     SERVICE_STATUS  serviceStatus;
     BOOL            bReturn;
 
 
    // Open Service Control Manager on the local machine...
     hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
 
     // Open the Service Control Manager for our driver service...
     hService = OpenService(hSCManager, "MSJDrvr", SERVICE_ALL_ACCESS);
 
     // Stop the driver.  Will return TRUE on success... 
     bReturn = ControlService(hService, SERVICE_CONTROL_STOP, &serviceStatus);
 
     // Delete the driver from the registry...
     if(bReturn == TRUE)
         bReturn = DeleteService(hService);
 
     // Close the SC Manager
     CloseServiceHandle(hService);
 
     // Close Service Control Manager
     CloseServiceHandle(hSCManager);
 
     return bReturn;
 }
 
 
 HANDLE LoadDriver(OSVERSIONINFO *os, BOOL *fNTDynaLoaded)
 {
     HANDLE hDevice;
 
     
     // Default to FALSE on informing the caller we've dynamically
     // loaded the Windows NT driver via the SCM.
     *fNTDynaLoaded = FALSE;
 
     // If we're under Windows NT...
     if(os->dwPlatformId == VER_PLATFORM_WIN32_NT)
     {
         // Try opening the device driver (in case it's static).  We'll
         // Deal with a failure in a moment...
         hDevice = CreateFile("\\\\.\\MSJDrvr",
                              GENERIC_READ | GENERIC_WRITE, 
                              FILE_SHARE_READ | FILE_SHARE_WRITE,
                              0,                     // Default security
                              OPEN_EXISTING,
                              FILE_FLAG_OVERLAPPED,  // Perform asynchronous I/O
                              0);                    // No template
         
         // If the device driver wasn't started, let's dynamically load it
         // here...
         if(hDevice == INVALID_HANDLE_VALUE)
         {
             // Load and start the driver...
             LoadDynamicNTDriver();
 
             // Open the driver...
             hDevice = CreateFile("\\\\.\\MSJDrvr",
                              GENERIC_READ | GENERIC_WRITE, 
                              FILE_SHARE_READ | FILE_SHARE_WRITE,
                              0,                     // Default security
                              OPEN_EXISTING,
                              FILE_FLAG_OVERLAPPED,  // Perform asynchronous I/O
                              0);                    // No template
             
             // Set the global to indicate that we've Dynaloaded.  This will
             // Cause DllMain to stop and unload on a process detach.
             *fNTDynaLoaded = TRUE;
         }
 
         return hDevice;
     }
     // Otherwise, assume Win95!
     else
     {
         // Dynaload the VxD...
         return CreateFile("\\\\.\\MSJDrvr.vxd", 0,0,0, CREATE_NEW,
                           FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_OVERLAPPED, 0);
     }
 }