2012年12月8日星期六

工作线程两例

/*
工作线程或者劳务线程,很重要,比起一般的内核线程。
它的运行环境不一般,在system中,特别是IRQL。
适用于小的任务,很方便。

made by correy
made at 2012.12.08
QQ:112426112
Email:kouleguan at hotmail dot com
Homepage:http://correy.webs.com
*/

#include <ntddk.h>

VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
    PDEVICE_OBJECT pdoNextDeviceObj = DriverObject->DeviceObject;//pdoGlobalDrvObj
    //IoDeleteSymbolicLink(&usSymlinkName);//本程序没有创建符号链接。
   
    while(pdoNextDeviceObj) // Delete all the device objects
    {
        PDEVICE_OBJECT pdoThisDeviceObj = pdoNextDeviceObj;
        pdoNextDeviceObj = pdoThisDeviceObj->NextDevice;
        IoDeleteDevice(pdoThisDeviceObj);
    }
}

IO_WORKITEM_ROUTINE WorkItem;
VOID WorkItem(__in PDEVICE_OBJECT  DeviceObject, __in_opt PVOID  Context)
{//如果不是同步的,有可能在DriverEntry函数运行结束后才运行到这里。
    DbgPrint("i am in WorkItem!\n");
    KeSetEvent((PKEVENT)Context, 0, FALSE);
}

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
    UNICODE_STRING usDeviceName;
    PDEVICE_OBJECT pdoDeviceObj = 0;
    NTSTATUS status = STATUS_UNSUCCESSFUL;
    PIO_WORKITEM g_piowi;
    KEVENT       g_ke;

    KdBreakPoint();//#define KdBreakPoint() DbgBreakPoint()
    DriverObject->DriverUnload = OnUnload;
   
    RtlInitUnicodeString(&usDeviceName, L"\\Device\\correy"); //必须创建设备,特别是Vista以前。
    status = IoCreateDevice(DriverObject, 0, &usDeviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &pdoDeviceObj);
    if(!NT_SUCCESS(status))
    {
        return status;
    }
 
    g_piowi = IoAllocateWorkItem(pdoDeviceObj); //参数是设备对象的指针,不是驱动对象的指针。不然WorkItem退出的时候,蓝屏。
    if (g_piowi == 0)
    {
        return STATUS_INSUFFICIENT_RESOURCES;
    }
   
    //IoInitializeWorkItem和IoAllocateWorkItem的功能差不多。
    //If the caller will later pass the work item to IoQueueWorkItem, IoObject must point to a device object.
    //IoInitializeWorkItem(DriverObject, &g_piowi);//Versions: Available on Windows Vista and later versions of Windows.

    KeInitializeEvent(&g_ke, SynchronizationEvent, FALSE);//设置事件以便同步,也可以不同步。就多3行代码。
    IoQueueWorkItem(g_piowi, &WorkItem, DelayedWorkQueue, &g_ke);
    KeWaitForSingleObject(&g_ke, Executive,KernelMode, FALSE, NULL);

    if (g_piowi)
    {
        IoFreeWorkItem(g_piowi);
    }

    return STATUS_SUCCESS;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*
其实这个办法跟简单,没有和设备关联,而且暂时没有被软件废弃。

made by correy
made at 2012.08.13
Email:kouleguan at hotmail dot com
Homepage:http://correy.webs.com
*/

#include <ntifs.h>

#define TAG 'tset' //本驱动在内存中的标志,即test.

typedef struct WorkItemContext{
    PWORK_QUEUE_ITEM Item;
    HANDLE Pid;
}WorkItemContext;

PVOID allocate(IN SIZE_T NumberOfBytes)
{
    PVOID p = NULL;

    //PAGED_CODE();
    if (KeGetCurrentIrql() > DISPATCH_LEVEL)
    {
        KdBreakPoint();//DbgBreakPoint()
    }

    /*
    Callers of ExAllocatePoolWithTag must be executing at IRQL <= DISPATCH_LEVEL.
    A caller executing at DISPATCH_LEVEL must specify a NonPagedXxx value for PoolType.
    A caller executing at IRQL <= APC_LEVEL can specify any POOL_TYPE value, but the IRQL and environment must also be considered for determining the page type.
    */

    p = ExAllocatePoolWithTag(NonPagedPool, NumberOfBytes, TAG);
    if (p == NULL ) {
        return p;
    }

    /*
    Warning  Memory that ExAllocatePoolWithTag allocates is uninitialized.
    A kernel-mode driver must first zero this memory if it is going to make it visible to user-mode software (to avoid leaking potentially privileged contents).
    */

    RtlZeroMemory(p, NumberOfBytes);

    return p;
}


VOID free(IN PVOID p)
{
    unsigned long r;

    /*
    Callers of ExFreePoolWithTag must be running at IRQL <= DISPATCH_LEVEL.
    A caller at DISPATCH_LEVEL must have specified a NonPagedXxx PoolType when the memory was allocated.
    Otherwise, the caller must be running at IRQL <= APC_LEVEL.
    */

    //PAGED_CODE();

    if (KeGetCurrentIrql() > DISPATCH_LEVEL)
    {
        KdBreakPoint();//DbgBreakPoint()
    }

    if (p) //防止多次释放导致的蓝屏。
    {      
        __try //防止传入非法的地址。
        {
            r = MmIsAddressValid(p);
            ExFreePoolWithTag(p, TAG);//KeGetCurrentIrql() > DISPATCH_LEVEL时依旧蓝屏。
        }
        __except (EXCEPTION_EXECUTE_HANDLER)
        {          
            r = GetExceptionCode();//啥也不做。
        }

        p = NULL;
    }
}

VOID MY_PWORKER_THREAD_ROUTINE (IN PVOID Parameter)
{
    WorkItemContext * pwic = (WorkItemContext *)Parameter;

    //做一些事。


    free(pwic->Item);
    free(pwic);
}

DRIVER_UNLOAD Unload;
VOID Unload(__in PDRIVER_OBJECT DriverObject)
{  

}

#pragma INITCODE
DRIVER_INITIALIZE DriverEntry;
NTSTATUS DriverEntry(__in struct _DRIVER_OBJECT * DriverObject, __in PUNICODE_STRING RegistryPath)
{
    NTSTATUS status = STATUS_SUCCESS;
    WorkItemContext * wic;

    KdBreakPoint();// == DbgBreakPoint()

    DriverObject->DriverUnload = Unload;  

    //之所以采用结构是因为传递的参数不是一个(实际应用的也不是一个),并且不是同步控制的。
    wic = (WorkItemContext *)allocate(sizeof(WorkItemContext));
    if (wic == NULL) {
        return status;
    }

    wic->Pid = 0;
    wic->Item = (PWORK_QUEUE_ITEM)allocate(sizeof(WORK_QUEUE_ITEM));
    if (wic->Item == NULL) {
        free(wic);
        return status;
    }

    ExInitializeWorkItem(wic->Item,MY_PWORKER_THREAD_ROUTINE,wic);
    ExQueueWorkItem(wic->Item, DelayedWorkQueue);
   
    return status;
}

没有评论:

发表评论