Path: news.mitre.org!blanket.mitre.org!philabs!newsjunkie.ans.net!newsfeeds.ans.net!news-was.dfn.de!news-spur1.maxwell.syr.edu!news.maxwell.syr.edu!newsfeed.ecrc.net!blackbush.xlink.net!fr.bosch.de!usenet
From: Christoph Minkwitz <Christoph.Minkwitz@himh1.hi.bosch.de>
Newsgroups: comp.os.ms-windows.programmer.nt.kernel-mode
Subject: Re: Kernel mode vs. Win32 events ?!?
Date: Thu, 27 Nov 1997 15:12:41 +0100
Organization: Robert Bosch GmbH
Lines: 793
Message-ID: <347D7FD8.A555AEF7@himh1.hi.bosch.de>
References: <01bcf9ba$07048020$8c1003c3@guyane>
NNTP-Posting-Host: hi212026.hi.bosch.de
Mime-Version: 1.0
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
X-Mailer: Mozilla 4.03 [en] (WinNT; I)

Stephane wrote:

> My kernel mode driver sets an event created by my Win32 code... But the PC
> hangs !!!
> Can this be done?
>
> Win32 deals with event handles and NT drivers deal with event object... Is
> there any compatibility?


Hi Stephane,

in an earlier writing Assaf <cefar@netvision.net.il> wrote:

#######################################################################
here it starts ...
#######################################################################

Here is a Microsoft Kernel Web Site sample of how to create an Event in
User Mode and pass the handle to a driver:

This is event.c:

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    Event.c

Abstract:

 This sample demonstrates one way that a Windows NT kernel-mode driver
 can share and explicitly signal an Event Object with a Win32 application.

   This sample uses the following method:
  The application creates an event object using CreateEvent().
  The app passes the event handle to the driver in a private IOCTL.
        The driver is running in the app's thread context during the IOCTL
so
        there is a valid user-mode handle at that time.
  The driver dereferences the user-mode handle into system space & saves
        the new system handle for later use.
  The driver signals the event via KeSetEvent() at IRQL <= DISPATCH_LEVEL.
  The driver MUST delete any driver references to the event object at
        Unload time.

    An alternative method would be to create a named event in the driver
via
    IoCreateNotificationEvent and then open the event in user mode. This
API
    however is new to Windows NT 4.0 so you can not use this method in your

    NT 3.5x drivers.

 Note that this sample's event can be signalled (almost) at will from
within
    the driver. A different event signal can be set when the driver is
setup to
    do asynchronous I/O, and it is opened with FILE_FLAG_OVERLAPPED, and an

    event handle is passed down in an OVERLAPPED struct from the app's
Read,
    Write, or DeviceIoControl. This different event signal is set by the
I/O
    Manager when the driver calls IoCompleteRequest on a pending Irp. This
type
    of Irp completion signal is not the purpose of this sample, hence the
lack of
    Irp queing.

Author:

    Jeff Midkiff    (jeffmi)    23-Jul-96

Enviroment:

    Kernel Mode Only

Revision History:

--*/


//
// INCLUDES
//
#include "ntddk.h"
#include "event.h"

//
// DEFINES
//
#define USER_NAME     L"\\DosDevices\\EVENT"
#define SYSTEM_NAME     L"\\Device\\EVENT"

//
// DATA
//
typedef struct _DEVICE_EXTENSION {
    KDPC Dpc;
    KTIMER Timer;
 HANDLE hEvent;

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;


//
// PROTOS
//
NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    );

NTSTATUS
Unload(
    IN PDRIVER_OBJECT DriverObject
    );

NTSTATUS
Dispatch(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    );

VOID
CustomTimerDPC(
    IN PKDPC Dpc,
    IN PVOID DeferredContext,
    IN PVOID SystemArgument1,
    IN PVOID SystemArgument2
    );

NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT  DriverObject,
    IN PUNICODE_STRING RegistryPath
    )

/*++

Routine Description:

    This routine gets called by the system to initialize the driver.

Arguments:

    DriverObject    - the system supplied driver object.
    RegistryPath    - the system supplied registry path for this driver.

Return Value:

    NTSTATUS

--*/

{

    PDEVICE_OBJECT  pDeviceObject;
    PDEVICE_EXTENSION pDeviceExtension;

    UNICODE_STRING  usSystemName;
    UNICODE_STRING  usUserName;

    NTSTATUS   status;


    KdPrint(("Event!DriverEntry - IN\n"));

    //
    // create the device object
    //
    RtlInitUnicodeString( &usSystemName, SYSTEM_NAME );

    status = IoCreateDevice(
                DriverObject,               // DriverObject
                sizeof( DEVICE_EXTENSION ), // DeviceExtensionSize
                &usSystemName,              // DeviceName
                FILE_DEVICE_UNKNOWN,        // DeviceType
                0,                          // DeviceCharacteristics
                TRUE,                       // Exclusive
                &pDeviceObject              // DeviceObject
                );

    if ( !NT_SUCCESS(status) )
    {
        KdPrint(("\tIoCreateDevice returned 0x%x\n", status));

        return( status );
    }

    //
    // Set up dispatch entry points for the driver.
    //
    DriverObject->MajorFunction[IRP_MJ_CREATE]   =
    DriverObject->MajorFunction[IRP_MJ_CLOSE]   =
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Dispatch;
    DriverObject->DriverUnload = Unload;

    //
    // Create a symbolic link into user mode for the driver.
    //
    RtlInitUnicodeString( &usUserName, USER_NAME );
    status = IoCreateSymbolicLink( &usUserName, &usSystemName );

    if ( !NT_SUCCESS(status) )
    {
        IoDeleteDevice( pDeviceObject );
        KdPrint(("\tIoCreateSymbolicLink returned 0x%x\n", status));

        return( status );
    }

    //
    // establish user-buffer access method
    //
    pDeviceObject->Flags |= DO_BUFFERED_IO;


    //
    // setup the device extension
    //
    pDeviceExtension = pDeviceObject->DeviceExtension;

    KeInitializeDpc(
        &pDeviceExtension->Dpc, // Dpc
        CustomTimerDPC,         // DeferredRoutine
        pDeviceObject           // DeferredContext
        );

    KeInitializeTimer(
        &pDeviceExtension->Timer  // Timer
        );

 pDeviceExtension->hEvent = NULL;

    KdPrint(("Event!DriverEntry - OUT\n"));

    return( status );
}


NTSTATUS
Unload(
    IN PDRIVER_OBJECT DriverObject
    )

/*++

Routine Description:

    This routine gets called to remove the driver from the system.

Arguments:

    DriverObject    - the system supplied driver object.

Return Value:

    NTSTATUS

--*/

{

    PDEVICE_OBJECT       pDeviceObject = DriverObject->DeviceObject;
    PDEVICE_EXTENSION    pDeviceExtension = pDeviceObject->DeviceExtension;
    UNICODE_STRING       usUserName;


    KdPrint(("Event!Unload\n"));

    //
 // dereference the event object or it will NEVER go away until reboot
 //
 if ( pDeviceExtension->hEvent )
  ObDereferenceObject( pDeviceExtension->hEvent );

    // Delete the user-mode symbolic link.
    RtlInitUnicodeString( &usUserName, USER_NAME );
    IoDeleteSymbolicLink( &usUserName );

    // Delete the DeviceObject
    IoDeleteDevice( pDeviceObject );

    return( STATUS_SUCCESS );
}


NTSTATUS
Dispatch(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This device control dispatcher handles IOCTLs.

Arguments:

    DeviceObject - Context for the activity.
    Irp          - The device control argument block.

Return Value:

    NTSTATUS

--*/

{

    PDEVICE_EXTENSION pDeviceExtension;
    PIO_STACK_LOCATION pIrpStack;
 PSET_EVENT   pSetEvent;

    ULONG               ulInformation = 0L;
 NTSTATUS   status = STATUS_NOT_IMPLEMENTED;


    KdPrint(("Event!Dispatch - IN\n"));

    pDeviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
    pIrpStack = IoGetCurrentIrpStackLocation( Irp );

 switch( pIrpStack->MajorFunction )
 {
  case IRP_MJ_CREATE:
  case IRP_MJ_CLOSE:
   KdPrint(("\t%s\n", (IRP_MJ_CREATE == pIrpStack->MajorFunction) ?
"IRP_MJ_CREATE" : "IRP_MJ_CLOSE"));

   status = STATUS_SUCCESS;
   break;

  case IRP_MJ_DEVICE_CONTROL:
   switch( pIrpStack->Parameters.DeviceIoControl.IoControlCode )
   {
    case IOCTL_SET_EVENT:
     KdPrint(("\tIOCTL_SET_EVENT\n"));

     if ( pIrpStack->Parameters.DeviceIoControl.InputBufferLength <
SIZEOF_SETEVENT )
     {
      // Parameters are invalid
      KdPrint(("\tSTATUS_INVALID_PARAMETER\n"));

      status = STATUS_INVALID_PARAMETER;
     }
     else
     {
      pSetEvent = (PSET_EVENT)Irp->AssociatedIrp.SystemBuffer;
      KdPrint(("\tuser-mode HANDLE = 0x%x\n",  pSetEvent->hEvent ));

      status = ObReferenceObjectByHandle( pSetEvent->hEvent,
               SYNCHRONIZE,
               NULL,
               KernelMode,
               &pDeviceExtension->hEvent,
               NULL
                                                            );
      if ( !NT_SUCCESS(status) )
      {

       KdPrint(("\tUnable to reference User-Mode Event object, Error =
0x%x\n", status));

      }
      else {

       KdPrint(("\tkernel-mode HANDLE = 0x%x\n",  pDeviceExtension->hEvent
));

       //
       // Start the timer to run the CustomTimerDPC in DueTime seconds to
       // simulate an interrupt (which would queue a DPC).
       // The user's event object is signaled in the DPC.
       //

       // ensure relative time for this sample
       if ( pSetEvent->DueTime.QuadPart > 0 )
        pSetEvent->DueTime.QuadPart = -(pSetEvent->DueTime.QuadPart);
       KdPrint(("\tDueTime  = %d\n",  pSetEvent->DueTime.QuadPart ));

       KeSetTimer(
        &pDeviceExtension->Timer, // Timer
        pSetEvent->DueTime,   // DueTime
        &pDeviceExtension->Dpc  // Dpc
        );

       status = STATUS_SUCCESS;
      }
     }
     break;

    default:
                    // should never hit this
           ASSERT(0);
     status = STATUS_NOT_IMPLEMENTED;
     break;

   } // switch IoControlCode
   break;

  default:
   // should never hit this
   ASSERT(0);
   status = STATUS_NOT_IMPLEMENTED;
   break;

 } // switch MajorFunction


    //
    // complete the Irp
    //
 Irp->IoStatus.Status = status;
 Irp->IoStatus.Information = ulInformation;
 IoCompleteRequest( Irp, IO_NO_INCREMENT );

    KdPrint(("Event!Dispatch - OUT\n"));
    return status;
}


VOID
CustomTimerDPC(
    IN PKDPC Dpc,
    IN PVOID DeferredContext,
    IN PVOID SystemArgument1,
    IN PVOID SystemArgument2
    )

/*++

Routine Description:

    This is the DPC associated with this drivers Timer object setup in
DriverEntry.

Arguments:

    Dpc             -   our DPC object associated with our Timer
    DeferredContext -   Context for the DPC that we setup in DriverEntry
    SystemArgument1 -
    SystemArgument2 -

Return Value:

    Nothing.

--*/

{

    PDEVICE_OBJECT      pDeviceObject = DeferredContext;
    PDEVICE_EXTENSION   pDeviceExtension = pDeviceObject->DeviceExtension;


 KdPrint(("Event!CustomTimerDPC - IN\n"));

 //
 // Signal the Event created user-mode
 //
 // Note:
 // Do not call KeSetEvent from your ISR;
 // you must call it at IRQL <= DISPATCH_LEVEL.
 // Your ISR should queue a DPC and the DPC can
 // then call KeSetEvent on the ISR's behalf.
 //
 KeSetEvent((PKEVENT)pDeviceExtension->hEvent, // Event
      0,                                   // Increment
      FALSE                                // Wait
      );

 // there is no Irp to complete here

 KdPrint(("Event!CustomTimerDPC - OUT\n"));

    return;
}


// EOF

here is event.h

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

   Event.h

Abstract:


Author:

   Jeff Midkiff    (jeffmi)    23-Jul-96

Enviroment:


Revision History:

--*/

#ifndef __EVENT__
#define __EVENT__


#include "devioctl.h"

typedef struct _SET_EVENT
{
    HANDLE  hEvent;
    LARGE_INTEGER DueTime; // requested DueTime in 100-nanosecond units

} SET_EVENT, *PSET_EVENT;

#define SIZEOF_SETEVENT sizeof(SET_EVENT)


#define IOCTL_SET_EVENT \
   CTL_CODE( FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS )


#endif // __EVENT__

here is eventuse.c

/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

 EventTest.c

Abstract:

 Simple console test app demonstrating how a Win32 app can share
 an event object with a kernel-mode driver. For more information
 on using Event Objects at the application level see the Win32 SDK.


Author:

   Jeff Midkiff    (jeffmi)    23-Jul-96

Enviroment:

   User Mode

Revision History:

--*/


//
// INCLUDES
//
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <winioctl.h>
#include <conio.h>

#include "event.h"


//
// MAIN
//
void __cdecl
main(
 int argc,
 char ** argv
 )
{
    BOOL    bStatus;
    HANDLE  hDevice;
    ULONG   ulReturnedLength;

    SET_EVENT setEvent;
 FLOAT fDelay;


 if ( (argc < 2) || (argv[1] == NULL) )
    {
        printf("event <delay>\n");
  printf("\twhere <delay> = time to delay the event signal in seconds.\n");
        exit(0);
    }
 sscanf( argv[1], "%f", &fDelay );

 //
 // open the device
 //
    hDevice = CreateFile(
                "\\\\.\\EVENT",                     // lpFileName
                GENERIC_READ | GENERIC_WRITE,       // dwDesiredAccess
                FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
                NULL,                               // lpSecurityAttributes
                OPEN_EXISTING,                      //
dwCreationDistribution
                0,         // dwFlagsAndAttributes
                NULL                                // hTemplateFile
                );

    if (hDevice == INVALID_HANDLE_VALUE)
    {
        printf("CreateFile error = %d\n", GetLastError() );
        exit(0);
    }


 //
 // set the event signal delay
 //
 setEvent.DueTime.QuadPart = -((LONGLONG)(fDelay * 10.0E6));// use relative
time for this sample


 //
 // test the driver for bad event handles
 //
 setEvent.hEvent = NULL;
 bStatus = DeviceIoControl(
      hDevice,    // Handle to device
      IOCTL_SET_EVENT,  // IO Control code
      &setEvent,       // Input Buffer to driver.
      SIZEOF_SETEVENT,  // Length of input buffer in bytes.
      NULL,           // Output Buffer from driver.
      0,                      // Length of output buffer in bytes.
      &ulReturnedLength,  // Bytes placed in buffer.
      NULL     // synchronous call
      );
 if ( !bStatus )
 {
  printf("Bad handle TEST returned code %d.\n\n", GetLastError() );
 }
 else {
  printf("we should never get here\n");
  exit(0);
 }


 //
 //
 //
 setEvent.hEvent = CreateEvent(
       NULL,   // lpEventAttributes
       TRUE,   // bManualReset
       FALSE,  // bInitialState
       NULL    // lpName
       );


 if ( !setEvent.hEvent )
 {
  printf("CreateEvent error = %d\n", GetLastError() );
 }
 else
 {

  printf("Event HANDLE = 0x%x\n",  setEvent.hEvent );
  printf("Press any key to exit.\n");
  while( !_kbhit() )
  {
   bStatus = DeviceIoControl(
       hDevice,    // Handle to device
       IOCTL_SET_EVENT,  // IO Control code
       &setEvent,       // Input Buffer to driver.
       SIZEOF_SETEVENT,  // Length of input buffer in bytes.
       NULL,           // Output Buffer from driver.
       0,                      // Length of output buffer in bytes.
       &ulReturnedLength,  // Bytes placed in buffer.
       NULL     // synchronous call
       );

   if ( !bStatus )
   {
    printf("Ioctl failed with code %d\n", GetLastError() );
    break;
   }
   else {
    printf("Waiting for Event...\n");

    WaitForSingleObject( setEvent.hEvent,
          INFINITE );

    printf("Event signalled.\n\n");

    ResetEvent( setEvent.hEvent);
    //printf("Event reset.\n");
   }
  }
 }

 //
 // close the driver
 //
    if ( !CloseHandle(hDevice) )
    {
        printf("Failed to close device.\n");
    }

    exit(0);
}


// EOF

Here is the readme.txt file:


This sample demonstrates one way that a Windows NT kernel-mode device
driver
can share and explicitly signal an Event Object with a Win32 application.
It is composed of two parts, a Windows NT kernel-mode device driver and a
Win32
console test application. Both are built using the Windows NT DDK.


Instructions:
-------------

1) Build the driver and test application in either the FREE or CHECKED
build environment:

        BLD

    Both the driver and application are put in %NTDDK%\LIB\*\FREE | CHECKED
on your build machine.


2) Copy the newly built driver to your Target machine's
%SystemRoot%\system32\drivers
directory. Copy the newly built application to your target machine.
Also copy the EVENT.INI file to your Target machine.


3) Update the Target machine's Registry by running REGINI.EXE on the
EVENT.INI file, i.e.:

        REGINI EVENT.INI

   This adds a driver key under the
   HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services tree in the
Registry.
   You can verify this by running REGEDIT32.EXE and looking at the \Event
key.


4) Reboot the Target machine for the Registry changes to take effect.
Your driver will not load until you reboot.


5) Load the driver from the command line:

        NET START EVENT


6) Run the test app from the command line:

        EVENT <DELAY>

            where DELAY = time to delay the Event signal in seconds.


7) Unload the driver from the command line:

        NET STOP EVENT


#######################################################################
... here it stops.
#######################################################################


hope it helps you. I tried it and it works fine. The waiting for that event
(in eventuse.c) should be done in a separate tread, but it's another story.

Christoph

