Async.c
/*
Async.c - 1999 James M. Finnegan - Microsoft Systems Journal
This module sends various requests the associated kernel-mode driver to
instruct the driver to trigger KM to user-mode notification mechanisms.
*/
#include <windows.h>
#include <winioctl.h>
#define FILE_DEVICE_UNKNOWN 0x00000022
#define IOCTL_UNKNOWN_BASE FILE_DEVICE_UNKNOWN
#define IOCTL_NOTIFY_KERNEL_EVENT CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0800,
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_NOTIFY_USER_EVENT CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0801,
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_NOTIFY_EMPTY_IRP CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0802,
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_NOTIFY_REGISTER_APC CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0803,
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_NOTIFY_REGISTER_APC_WITH_KM_ALERTING \
CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0804, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
extern HANDLE hDriver;
BOOL WINAPI KernelModeEvent(DWORD t)
{
HANDLE KnotifyEvent;
DWORD dwBytesReturned;
BOOL bReturnCode = FALSE;
OVERLAPPED ov={0};
// Create an event handle for async notification
// from our driver
ov.hEvent = CreateEvent(NULL, // Default security
TRUE, // Manual reset
FALSE, // non-signaled state
NULL); // No name
// Tell the driver to signal its event...
bReturnCode = DeviceIoControl(hDriver,
IOCTL_NOTIFY_KERNEL_EVENT,
0, 0,
0, 0,
&dwBytesReturned,
&ov);
// Wait here for the event handle to be set, indicating
// that the IOCTL processing is complete.
bReturnCode = GetOverlappedResult(hDriver, &ov,
&dwBytesReturned, TRUE);
CloseHandle(ov.hEvent);
// Get a handle to the named kernel event and wait on it!
KnotifyEvent = OpenEvent(SYNCHRONIZE, FALSE, "KnotifyEvent");
WaitForSingleObject(KnotifyEvent, INFINITE);
MessageBox(NULL,"Kernel-mode event has been signaled!","Unotify",MB_OK);
return 0;
}
BOOL WINAPI UserModeEvent(DWORD t)
{
HANDLE UserModeEvent;
DWORD dwBytesReturned;
BOOL bReturnCode = FALSE;
OVERLAPPED ov={0};
// Create an event handle that the KM driver can signal
UserModeEvent = CreateEvent(NULL, // Default security
TRUE, // Manual reset
FALSE, // non-signaled state
NULL); // No name
// Create an event handle for async notification
// from our driver
ov.hEvent = CreateEvent(NULL, // Default security
TRUE, // Manual reset
FALSE, // non-signaled state
NULL); // No name
// Tell the driver to signal its event...
bReturnCode = DeviceIoControl(hDriver,
IOCTL_NOTIFY_USER_EVENT,
&UserModeEvent, sizeof(HANDLE),
0, 0,
&dwBytesReturned,
&ov);
// Wait here for the event handle to be set, indicating
// that the IOCTL processing is complete.
bReturnCode = GetOverlappedResult(hDriver, &ov,
&dwBytesReturned, TRUE);
CloseHandle(ov.hEvent);
WaitForSingleObject(UserModeEvent, INFINITE);
MessageBox(NULL,"User-mode event has been signaled!","Unotify",MB_OK);
CloseHandle(UserModeEvent);
return 0;
}
BOOL WINAPI EmptyIRP(DWORD t)
{
DWORD dwBytesReturned;
BOOL bReturnCode = FALSE;
OVERLAPPED ov={0};
// Create an event handle for async notification
// from our driver
ov.hEvent = CreateEvent(NULL, // Default security
TRUE, // Manual reset
FALSE, // non-signaled state
NULL); // No name
// Issue an empty IRP to the driver. The driver will mark it pending till
// it's ready to complete it
bReturnCode = DeviceIoControl(hDriver,
IOCTL_NOTIFY_EMPTY_IRP,
0, 0,
0, 0,
&dwBytesReturned,
&ov);
// Wait here for the event handle to be set, indicating
// that the IOCTL processing is complete.
bReturnCode = GetOverlappedResult(hDriver, &ov,
&dwBytesReturned, TRUE);
CloseHandle(ov.hEvent);
MessageBox(NULL,"Empty IRP has been completed!","Unotify",MB_OK);
return 0;
}
void ApcCallback(PVOID NormalContext, PVOID SystemArgument1,
PVOID SystemArgument2)
{
char Message[80];
wsprintf(Message,"APC Callback!\n P1 == %lx\n P2 == %lx\n P3 == %lx",
NormalContext, SystemArgument1, SystemArgument2);
MessageBox(NULL, Message, "Unotify", MB_OK);
}
void ApcDispatch()
{
DWORD dwBytesReturned;
BOOL bReturnCode = FALSE;
OVERLAPPED ov={0};
DWORD pfnApc = (DWORD)&ApcCallback;
// Create an event handle for async notification
// from our driver
ov.hEvent = CreateEvent(NULL, // Default security
TRUE, // Manual reset
FALSE, // non-signaled state
NULL); // No name
// Tell the driver to signal its event...
bReturnCode = DeviceIoControl(hDriver,
IOCTL_NOTIFY_REGISTER_APC,
&pfnApc, sizeof(DWORD),
0, 0,
&dwBytesReturned,
&ov);
// Wait here for the event handle to be set, indicating
// that the IOCTL processing is complete.
bReturnCode = GetOverlappedResult(hDriver, &ov,
&dwBytesReturned, TRUE);
CloseHandle(ov.hEvent);
MessageBox(NULL,"APC Dispatched!","Unotify",MB_OK);
// Force the thread into being alertable...
SleepEx(INFINITE, TRUE);
}
void ApcDispatchWithKMAlertHack()
{
DWORD dwBytesReturned;
BOOL bReturnCode = FALSE;
OVERLAPPED ov={0};
DWORD pfnApc = (DWORD)&ApcCallback;
// Create an event handle for async notification
// from our driver
ov.hEvent = CreateEvent(NULL, // Default security
TRUE, // Manual reset
FALSE, // non-signaled state
NULL); // No name
// Tell the driver to signal its event...
bReturnCode = DeviceIoControl(hDriver,
IOCTL_NOTIFY_REGISTER_APC_WITH_KM_ALERTING,
&pfnApc, sizeof(DWORD),
0, 0,
&dwBytesReturned,
&ov);
// Wait here for the event handle to be set, indicating
// that the IOCTL processing is complete.
bReturnCode = GetOverlappedResult(hDriver, &ov,
&dwBytesReturned, TRUE);
CloseHandle(ov.hEvent);
MessageBox(NULL,"APC Dispatched! Look, no user-mode alert!","Unotify",MB_OK);
}
DynamIc.c
/*
Dynamic.c - 1998, 1999 James M. Finnegan - Microsoft Systems Journal
This module implements the functionality to dynamically load and
unload Windows NT drivers 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,"\\Knotify.sys");
// Install drive in the registry and start it...
bReturn = InstallAndStartDriver(hSCManager, "Knotify", 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, "Knotify", 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 if 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("\\\\.\\Knotify",
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("\\\\.\\Knotify",
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;
}
}
Unotify.c
/*
Unotify.c - 1999 James M. Finnegan - Microsoft Systems Journal
This is the main module for demonstrating asynchronous notification
from kernel mode.
*/
#include <windows.h>
#include <stdio.h>
#include "resource.h"
LRESULT WINAPI WndProc(HWND,UINT,WPARAM,LPARAM);
HANDLE hDriver = INVALID_HANDLE_VALUE;
// External functions in Dynamic.c
extern HANDLE LoadDriver(OSVERSIONINFO *, BOOL *);
extern BOOL UnloadDynamicNTDriver();
// External functions in Async.c
extern BOOL WINAPI KernelModeEvent(DWORD);
extern BOOL WINAPI UserModeEvent(DWORD);
extern BOOL WINAPI EmptyIRP(DWORD);
extern void ApcDispatch();
extern void ApcDispatchWithKMAlertHack();
int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpszCmdLine, int nCmdShow)
{
static char szAppName[]="UNotify";
HWND hWnd;
MSG msg;
WNDCLASS wndclass;
if(!hPrevInstance)
{
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = NULL;
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
wndclass.lpszClassName = szAppName;
if(!RegisterClass(&wndclass))
return -1;
}
// Create the main window
hWnd = CreateWindow(szAppName,"MSJ Kernel Mode Async Notification App",
WS_OVERLAPPED | WS_THICKFRAME | WS_MINIMIZEBOX |
WS_MAXIMIZEBOX |WS_SYSMENU | WS_CLIPCHILDREN,
CW_USEDEFAULT,CW_USEDEFAULT,
CW_USEDEFAULT,CW_USEDEFAULT,
0,0,hInstance,NULL);
ShowWindow(hWnd,nCmdShow);
UpdateWindow(hWnd);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
VOID CenterWindow(HWND hWnd)
{
RECT rect;
WORD wWidth,
wHeight;
GetWindowRect(hWnd,&rect);
wWidth =GetSystemMetrics(SM_CXSCREEN);
wHeight=GetSystemMetrics(SM_CYSCREEN);
MoveWindow(hWnd,(wWidth/2) - ((rect.right - rect.left)/2),
(wHeight/2) - ((rect.bottom - rect.top) /2),
rect.right - rect.left,
rect.bottom - rect.top,
FALSE);
}
LRESULT WINAPI WndProc(HWND hWnd,UINT uMessage,WPARAM wParam,LPARAM lParam)
{
static OSVERSIONINFO os={0};
static BOOL fNTDynaLoaded;
HANDLE hThread;
DWORD dwThread;
switch(uMessage)
{
case WM_CREATE:
CenterWindow(hWnd);
// Get the current OS information.
os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
GetVersionEx(&os);
hDriver = LoadDriver(&os, &fNTDynaLoaded);
break;
case WM_COMMAND:
switch(wParam)
{
case IDM_KERNEL_EVENT:
if(!(hThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0,
(LPTHREAD_START_ROUTINE) KernelModeEvent, NULL, 0,
&dwThread)))
MessageBox(NULL,"Cannot create thread!","Error",MB_OK);
break;
case IDM_USER_EVENT:
if(!(hThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0,
(LPTHREAD_START_ROUTINE) UserModeEvent, NULL, 0,
&dwThread)))
MessageBox(NULL,"Cannot create thread!","Error",MB_OK);
break;
case IDM_EMPTY_IRP:
if(!(hThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0,
(LPTHREAD_START_ROUTINE) EmptyIRP, NULL, 0, &dwThread)))
MessageBox(NULL,"Cannot create thread!","Error",MB_OK);
break;
case IDM_APC:
ApcDispatch();
break;
case IDM_APC_KM_THREAD_ALERTING:
ApcDispatchWithKMAlertHack();
break;
case IDM_EXIT:
PostMessage(hWnd, WM_CLOSE, 0, 0L);
break;
}
break;
case WM_DESTROY:
CloseHandle(hDriver);
// If the Windows NT driver was previously dynamically loaded, unload
// it here!
if(fNTDynaLoaded)
UnloadDynamicNTDriver();
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd,uMessage,wParam,lParam);
break;
}
return 0L;
}
Knotify.c
/*
Knotify.c - 1999 James M. Finnegan - Microsoft Systems Journal
This module implements the kernel code to demonstrate notification
techniques to user-mode Win32-based applications
*/
#include "ntddk.h"
#include <stdio.h>
#define FILE_DEVICE_UNKNOWN 0x00000022
#define IOCTL_UNKNOWN_BASE FILE_DEVICE_UNKNOWN
#define IOCTL_NOTIFY_KERNEL_EVENT CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0800,
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_NOTIFY_USER_EVENT CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0801,
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_NOTIFY_EMPTY_IRP CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0802,
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_NOTIFY_REGISTER_APC CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0803,
METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)
#define IOCTL_NOTIFY_REGISTER_APC_WITH_KM_ALERTING \ CTL_CODE( IOCTL_UNKNOWN_BASE, 0x0804, METHOD_BUFFERED, FILE_READ_ACCESS |
FILE_WRITE_ACCESS)
// Definitions for Windows NT-supplied APC routines. These are exported in the
// import libraries, but are not in NTDDK.H
void KeInitializeApc(PKAPC Apc,
PKTHREAD Thread,
CCHAR ApcStateIndex,
PKKERNEL_ROUTINE KernelRoutine,
PKRUNDOWN_ROUTINE RundownRoutine,
PKNORMAL_ROUTINE NormalRoutine,
KPROCESSOR_MODE ApcMode,
PVOID NormalContext);
void KeInsertQueueApc(PKAPC Apc,
PVOID SystemArgument1,
PVOID SystemArgument2,
UCHAR unknown);
void KnotifyUnloadDriver(PDRIVER_OBJECT DriverObject);
NTSTATUS KnotifyDispatchCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
NTSTATUS KnotifyDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp);
typedef struct _DEVICE_EXTENSION
{
PDEVICE_OBJECT DeviceObject;
HANDLE NotifyHandle;
PKEVENT NotifyEvent;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION ;
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
/*++
Routine Description:
This routine is called when the driver is loaded by Windows 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;
UNICODE_STRING uszNotifyEventString;
PDEVICE_OBJECT pDeviceObject;
PDEVICE_EXTENSION extension;
// Point uszDriverString at the driver name
RtlInitUnicodeString(&uszDriverString, L"\\Device\\Knotify");
// Create and initialize device object
ntStatus = IoCreateDevice(DriverObject,
sizeof(DEVICE_EXTENSION),
&uszDriverString,
FILE_DEVICE_UNKNOWN,
0,
FALSE,
&pDeviceObject);
if(ntStatus != STATUS_SUCCESS)
return ntStatus;
// Assign extension variable...
extension = pDeviceObject->DeviceExtension;
// Point uszDeviceString at the device name
RtlInitUnicodeString(&uszDeviceString, L"\\DosDevices\\Knotify");
// 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 = KnotifyUnloadDriver;
DriverObject->MajorFunction[IRP_MJ_CREATE] = KnotifyDispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = KnotifyDispatchCreateClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = KnotifyDispatchIoctl;
// Create event for user-mode processes to monitor
RtlInitUnicodeString(&uszNotifyEventString,
L"\\BaseNamedObjects\\KnotifyEvent");
extension->NotifyEvent = IoCreateNotificationEvent(&uszNotifyEventString,
&extension->NotifyHandle);
KeClearEvent(extension->NotifyEvent);
// Return success
return ntStatus;
}
NTSTATUS KnotifyDispatchCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information=0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return(STATUS_SUCCESS);
}
PWORK_QUEUE_ITEM pItem;
void KSignalEvent(PKEVENT NotifyEvent)
{
LARGE_INTEGER Timeout;
// AET - 143
Timeout.QuadPart = -5000000;
// Delay execution to show "asynchronous" processing
KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
KeSetEvent(NotifyEvent, 0, FALSE);
ExFreePool(pItem);
}
void KDelayIRPCompletion(PIRP Irp)
{
LARGE_INTEGER Timeout;
Timeout.QuadPart = -5000000;
// Delay execution to show "asynchronous" processing
KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
Irp->IoStatus.Status = STATUS_SUCCESS;
// Set # of bytes to copy back to user mode...
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
ExFreePool(pItem);
}
void KMApcCallback(PKAPC Apc, PKNORMAL_ROUTINE NormalRoutine,
PVOID NormalContext, PVOID SystemArgument1,
PVOID SystemArgument2)
{
ExFreePool(Apc);
return;
}
NTSTATUS KnotifyDispatchIoctl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
{
NTSTATUS ntStatus;
PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
PDEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
switch(irpStack->Parameters.DeviceIoControl.IoControlCode)
{
case IOCTL_NOTIFY_KERNEL_EVENT:
{
KeClearEvent(extension->NotifyEvent);
pItem = ExAllocatePool(NonPagedPool, sizeof(WORK_QUEUE_ITEM));
// Queue execution to a system thread to permit this IRP to
// complete...
ExInitializeWorkItem(pItem, KSignalEvent, extension->NotifyEvent);
ExQueueWorkItem(pItem, DelayedWorkQueue);
ntStatus = STATUS_SUCCESS;
break;
}
case IOCTL_NOTIFY_USER_EVENT:
{
PKEVENT UserEvent;
// Obtain a kernel pointer to the user-mode-created event handle...
ObReferenceObjectByHandle(*((
PHANDLE)Irp->AssociatedIrp.SystemBuffer),
0,
(POBJECT_TYPE) NULL,
UserMode,
(PVOID)&UserEvent,
(POBJECT_HANDLE_INFORMATION) NULL);
pItem = ExAllocatePool(NonPagedPool, sizeof(WORK_QUEUE_ITEM));
// Queue execution to a system thread to permit this IRP to
// complete...
ExInitializeWorkItem(pItem, KSignalEvent, UserEvent);
ExQueueWorkItem(pItem, DelayedWorkQueue);
ntStatus = STATUS_SUCCESS;
break;
}
case IOCTL_NOTIFY_EMPTY_IRP:
pItem = ExAllocatePool(NonPagedPool, sizeof(WORK_QUEUE_ITEM));
// Queue execution to a system thread to permit this IRP to
// complete...
ExInitializeWorkItem(pItem, KDelayIRPCompletion, Irp);
ExQueueWorkItem(pItem, DelayedWorkQueue);
// Mark the IRP pending. The driver will complete it later...
IoMarkIrpPending(Irp);
ntStatus = STATUS_PENDING;
break;
case IOCTL_NOTIFY_REGISTER_APC:
{
PKAPC Apc;
ULONG *UserRoutine = (ULONG *)Irp->AssociatedIrp.SystemBuffer;
Apc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
KeInitializeApc(Apc,
KeGetCurrentThread(),
0,
(PKKERNEL_ROUTINE)&KMApcCallback, // kernel-mode routine
0, // rundown routine
(PKNORMAL_ROUTINE)*UserRoutine, // user-mode routine
UserMode, (PVOID)(ULONG)1);
KeInsertQueueApc(Apc, (PVOID)(ULONG)2, (PVOID)(ULONG)3, 0);
ntStatus = STATUS_SUCCESS;
break;
}
case IOCTL_NOTIFY_REGISTER_APC_WITH_KM_ALERTING:
{
KEVENT event;
PKTHREAD pThread = KeGetCurrentThread();
PKAPC Apc;
ULONG *UserRoutine = (ULONG *)Irp->AssociatedIrp.SystemBuffer;
LARGE_INTEGER Timeout;
Timeout.QuadPart = 0;
Apc = ExAllocatePool(NonPagedPool, sizeof(KAPC));
KeInitializeApc(Apc,
KeGetCurrentThread(),
0,
(PKKERNEL_ROUTINE)&KMApcCallback, // kernel-mode routine
0, // rundown routine
(PKNORMAL_ROUTINE)*UserRoutine, // user-mode routine
UserMode, (PVOID)(ULONG)1);
KeInsertQueueApc(Apc, (PVOID)(ULONG)2, (PVOID)(ULONG)3, 0);
// Cheezy way to force the thread into being alertable :-)
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
KeWaitForSingleObject(&event, Executive, UserMode, TRUE, &Timeout);
ntStatus = STATUS_SUCCESS;
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;
if(ntStatus != STATUS_PENDING)
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return ntStatus;
}
void KnotifyUnloadDriver(PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING uszDeviceString;
IoDeleteDevice(DriverObject->DeviceObject);
RtlInitUnicodeString(&uszDeviceString, L"\\DosDevices\\Knotify");
IoDeleteSymbolicLink(&uszDeviceString);
}
Figure 2 Using the OVERLAPPED Structure
OVERLAPPED ov={0};
•
•
•
// Create an event handle for async notification
// from our driver
ov.hEvent = CreateEvent(NULL, // Default security
TRUE, // Manual reset
FALSE, // non-signaled state
NULL); // No name
// Tell the driver to signal its event...
bReturnCode = DeviceIoControl(hDriver,
IOCTL_NOTIFY_USER_EVENT,
&UserModeEvent, sizeof(HANDLE),
0, 0,
&dwBytesReturned,
&ov);
// Wait here for the event handle to be set, indicating
// that the IOCTL processing is complete.
bReturnCode = GetOverlappedResult(hDriver, &ov,
&dwBytesReturned, TRUE);
CloseHandle(ov.hEvent);
Figure 3 APC Snippet from NTDDK.H
typedef CCHAR KPROCESSOR_MODE;
typedef enum _MODE {
KernelMode,
UserMode,
MaximumMode
} MODE;
// end_ntndis
//
// APC function types
//
// Put in an empty definition for the KAPC so that the
// routines can reference it before it is declared.
struct _KAPC;
typedef
VOID
(*PKNORMAL_ROUTINE) (
IN PVOID NormalContext,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
);
typedef
VOID
(*PKKERNEL_ROUTINE) (
IN struct _KAPC *Apc,
IN OUT PKNORMAL_ROUTINE *NormalRoutine,
IN OUT PVOID *NormalContext,
IN OUT PVOID *SystemArgument1,
IN OUT PVOID *SystemArgument2
);
typedef
VOID
(*PKRUNDOWN_ROUTINE) (
IN struct _KAPC *Apc
);
typedef
BOOLEAN
(*PKSYNCHRONIZE_ROUTINE) (
IN PVOID SynchronizeContext
);
typedef
BOOLEAN
(*PKTRANSFER_ROUTINE) (
VOID
);
//
//
// Asynchronous Procedure Call (APC) object
//
typedef struct _KAPC {
CSHORT Type;
CSHORT Size;
ULONG Spare0;
struct _KTHREAD *Thread;
LIST_ENTRY ApcListEntry;
PKKERNEL_ROUTINE KernelRoutine;
PKRUNDOWN_ROUTINE RundownRoutine;
PKNORMAL_ROUTINE NormalRoutine;
PVOID NormalContext;
//
// N.B. The following two members MUST be together.
//
PVOID SystemArgument1;
PVOID SystemArgument2;
CCHAR ApcStateIndex;
KPROCESSOR_MODE ApcMode;
BOOLEAN Inserted;
} KAPC, *PKAPC, *RESTRICTED_POINTER PRKAPC;