zoukankan      html  css  js  c++  java
  • 27、Windows内核编程,IRP的同步(1)

    对设备的任何操作都会转化为IRP请求,而IRP一般都是由操作系统异步发送的。但是有时需要同步来避免逻辑错误。同步方法有:StartIO例程,使用中断服务例程等。

    1、应用程序对设备的同步异步操作

    1)同步操作原理

    大部分IRP是由应用程序的Win32 API发起。这些函数本身就支持同步异步操作。如ReadFile,WriteFile,DeviceIoControl等。

    ScreenShot007

    图示 IRP同步操作示意图P250

    2)同步操作设备

    CreateFile,其参数dwFlagsAndAttributes 是同步异步的关键。没有设置FILE_FLAG_OVERLAPPED为同步,否则为异步。

    再如ReadFile,其参数lpOverlapped 如果设为NULL,则为同步。

    代码
    1 #include <windows.h>
    2 #include <stdio.h>
    3
    4 #define BUFFER_SIZE 512
    5 int main()
    6 {
    7 HANDLE hDevice =
    8 CreateFile("test.dat",
    9 GENERIC_READ | GENERIC_WRITE,
    10 0,
    11 NULL,
    12 OPEN_EXISTING,
    13 FILE_ATTRIBUTE_NORMAL,//此处没有设置FILE_FLAG_OVERLAPPED
    14 NULL );
    15
    16 if (hDevice == INVALID_HANDLE_VALUE)
    17 {
    18 printf("Read Error\n");
    19 return 1;
    20 }
    21
    22 UCHAR buffer[BUFFER_SIZE];
    23 DWORD dwRead;
    24 ReadFile(hDevice,buffer,BUFFER_SIZE,&dwRead,NULL);//这里没有设置OVERLAP参数
    25
    26 CloseHandle(hDevice);
    27
    28 return 0;
    29 }
    示例代码 P252

    3)异步操作设备

    法一:通过OVERLAPPED 结构体。

    typedef struct _OVERLAPPED {

    ULONG_PTR Internal;

    ULONG_PTR InternalHigh;

    DWORD Offset;

    DWORD OffsetHigh;

    HANDLE hEvent;

    } OVERLAPPED;

    第三个参数:操作设备会指定一个偏移量,从设备的偏移量进行读取。该偏移量用一个64位整形表示。Offset为该偏移量的低32位整形,OffsetHigh为高32位整形。

    hEvent用于操作完成后通知应用程序。我们可以初始化其为未激发状态,当操作设备结束后,即在驱动中调用IoCompleteRequest后,设备该事件激发态。

    OVERLAPPED 结构使用前要清0,并为其创建事件。

    代码
    1 #include <windows.h>
    2 #include <stdio.h>
    3
    4 #define BUFFER_SIZE 512
    5 //假设该文件大于或等于BUFFER_SIZE
    6
    7 #define DEVICE_NAME "test.dat"
    8 int main()
    9 {
    10 HANDLE hDevice =
    11 CreateFile("test.dat",
    12 GENERIC_READ | GENERIC_WRITE,
    13 0,
    14 NULL,
    15 OPEN_EXISTING,
    16 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//此处设置FILE_FLAG_OVERLAPPED
    17 NULL );
    18
    19 if (hDevice == INVALID_HANDLE_VALUE)
    20 {
    21 printf("Read Error\n");
    22 return 1;
    23 }
    24
    25 UCHAR buffer[BUFFER_SIZE];
    26 DWORD dwRead;
    27
    28 //初始化overlap使其内部全部为零
    29 OVERLAPPED overlap={0};
    30
    31 //创建overlap事件
    32 overlap.hEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
    33
    34 //这里没有设置OVERLAP参数,因此是异步操作
    35 ReadFile(hDevice,buffer,BUFFER_SIZE,&dwRead,&overlap);
    36
    37 //做一些其他操作,这些操作会与读设备并行执行
    38
    39 //等待读设备结束
    40 WaitForSingleObject(overlap.hEvent,INFINITE);
    41
    42 CloseHandle(hDevice);
    43
    44 return 0;
    45 }
    46
    47
    示例代码 P254

    法二:ReadFileExWriteFileEx 专门被用来异步操作。

    BOOL WriteFileEx(

    HANDLE hFile, // handle to output file

    LPCVOID lpBuffer, // data buffer

    DWORD nNumberOfBytesToWrite, // number of bytes to write

    LPOVERLAPPED lpOverlapped, // overlapped buffer

    LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine // completion routine

    );

    lpOverlapped不需要提供事件句柄。

    ReadFileEx 将读请求传递到驱动程序后立即返回。驱动程序在结束读操作后,会通过调用ReadFileEx提供的回调函数(Call Back Function)lpCompletionRoutine 。类似于一个软中断。Windows将这种机制称为异步过程调用APC(asynchronous procedure call )。只有线程于Alert状态时,回调函数才能被调用。多个函数使系统进行Alert状态:SleepExWaitForMultipleObjectsExWaitForSingleObjectExetc

    OS一旦结束读取操作,就会把相应的completion routine插入到APC队列中。当OS进入Alert状态后,会枚举当前线程的APC队列。

    代码
    1 #include <windows.h>
    2 #include <stdio.h>
    3
    4 #define DEVICE_NAME "test.dat"
    5 #define BUFFER_SIZE 512
    6 //假设该文件大于或等于BUFFER_SIZE
    7
    8 VOID CALLBACK MyFileIOCompletionRoutine(
    9 DWORD dwErrorCode, // 对于此次操作返回的状态
    10 DWORD dwNumberOfBytesTransfered, // 告诉已经操作了多少字节,也就是在IRP里的Infomation
    11 LPOVERLAPPED lpOverlapped // 这个数据结构
    12 )
    13 {
    14 printf("IO operation end!\n");
    15 }
    16
    17 int main()
    18 {
    19 HANDLE hDevice =
    20 CreateFile("test.dat",
    21 GENERIC_READ | GENERIC_WRITE,
    22 0,
    23 NULL,
    24 OPEN_EXISTING,
    25 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//此处设置FILE_FLAG_OVERLAPPED
    26 NULL );
    27
    28 if (hDevice == INVALID_HANDLE_VALUE)
    29 {
    30 printf("Read Error\n");
    31 return 1;
    32 }
    33
    34 UCHAR buffer[BUFFER_SIZE];
    35
    36 //初始化overlap使其内部全部为零
    37 //不用初始化事件!!
    38 OVERLAPPED overlap={0};
    39
    40 //这里没有设置OVERLAP参数,因此是异步操作
    41 ReadFileEx(hDevice, buffer, BUFFER_SIZE,&overlap,MyFileIOCompletionRoutine);
    42
    43 //做一些其他操作,这些操作会与读设备并行执行
    44
    45 //进入alterable
    46 SleepEx(0,TRUE);
    47
    48 CloseHandle(hDevice);
    49
    50 return 0;
    51 }
    52
    53
    示例代码 P255

    2IRP的同步完成和异步完成

    两种方法,一种是在派遣函数中直接结束IRP,称为同步方法。另外一种是在派遣函数中不结束IRP,而让派遣函数直接返回,IRP在以后某个时候再进行处理,也称为异步方法。

    比如在ReadFile中同步时,派遣函数在调用IoCompleteRequest时,IoCompleteRequest内部会设置IRPUserEvent事件。

    如果在ReadFile异步时,ReadFile不创建事件,但是接收overlap参数,IoCompleteRequest内部会设置overlap提供的事件。

    ReadFileEx异步中,IoCompleteRequestReadFileEx提供的回调函数插入到APC队列中。

    异步时,通过GetLastError()得到ERROR_IO_INCOMPLETE。如果派遣函数不调用IoCompleteRequest,此时IRP处于挂起状态。处理方法见示例代码中。

    同时,应用程序关闭设置时产生IRP_MJ_CLEANUP类型的IRP。可以在IRP_MJ_CLEANUP的派遣函数中调用IoCompleteRequest来结束那些挂起的IRP请求。

    代码
    //.h
    #pragma once

    #ifdef __cplusplus
    extern "C"
    {
    #endif
    #include
    <NTDDK.h>
    #ifdef __cplusplus
    }
    #endif

    #define PAGEDCODE code_seg("PAGE")
    #define LOCKEDCODE code_seg()
    #define INITCODE code_seg("INIT")

    #define PAGEDDATA data_seg("PAGE")
    #define LOCKEDDATA data_seg()
    #define INITDATA data_seg("INIT")

    #define arraysize(p) (sizeof(p)/sizeof((p)[0]))

    typedef
    struct _DEVICE_EXTENSION {
    PDEVICE_OBJECT pDevice;
    UNICODE_STRING ustrDeviceName;
    //设备名称
    UNICODE_STRING ustrSymLinkName; //符号链接名
    PLIST_ENTRY pIRPLinkListHead;
    } DEVICE_EXTENSION,
    *PDEVICE_EXTENSION;

    typedef
    struct _MY_IRP_ENTRY
    {
    PIRP pIRP;
    LIST_ENTRY ListEntry;
    } MY_IRP_ENTRY,
    *PMY_IRP_ENTRY;

    // 函数声明

    NTSTATUS CreateDevice (IN PDRIVER_OBJECT pDriverObject);
    VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject);
    NTSTATUS HelloDDKDispatchRoutin(IN PDEVICE_OBJECT pDevObj,
    IN PIRP pIrp);
    NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
    IN PIRP pIrp);
    NTSTATUS HelloDDKCleanUp(IN PDEVICE_OBJECT pDevObj,
    IN PIRP pIrp);
    //.cpp
    #include "Driver.h"

    /************************************************************************
    * 函数名称:DriverEntry
    * 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象
    * 参数列表:
    pDriverObject:从I/O管理器中传进来的驱动对象
    pRegistryPath:驱动程序在注册表的中的路径
    * 返回 值:返回初始化驱动状态
    ************************************************************************
    */
    #pragma INITCODE
    extern "C" NTSTATUS DriverEntry (
    IN PDRIVER_OBJECT pDriverObject,
    IN PUNICODE_STRING pRegistryPath )
    {
    NTSTATUS status;
    KdPrint((
    "Enter DriverEntry\n"));

    //设置卸载函数
    pDriverObject->DriverUnload = HelloDDKUnload;

    //设置派遣函数
    pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutin;
    pDriverObject
    ->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutin;
    pDriverObject
    ->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutin;
    pDriverObject
    ->MajorFunction[IRP_MJ_READ] = HelloDDKRead;
    pDriverObject
    ->MajorFunction[IRP_MJ_CLEANUP] = HelloDDKCleanUp;
    pDriverObject
    ->MajorFunction[IRP_MJ_DEVICE_CONTROL] = HelloDDKDispatchRoutin;
    pDriverObject
    ->MajorFunction[IRP_MJ_SET_INFORMATION] = HelloDDKDispatchRoutin;
    pDriverObject
    ->MajorFunction[IRP_MJ_SHUTDOWN] = HelloDDKDispatchRoutin;
    pDriverObject
    ->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = HelloDDKDispatchRoutin;

    //创建驱动设备对象
    status = CreateDevice(pDriverObject);

    KdPrint((
    "Leave DriverEntry\n"));
    return status;
    }

    /************************************************************************
    * 函数名称:CreateDevice
    * 功能描述:初始化设备对象
    * 参数列表:
    pDriverObject:从I/O管理器中传进来的驱动对象
    * 返回 值:返回初始化状态
    ************************************************************************
    */
    #pragma INITCODE
    NTSTATUS CreateDevice (
    IN PDRIVER_OBJECT pDriverObject)
    {
    NTSTATUS status;
    PDEVICE_OBJECT pDevObj;
    PDEVICE_EXTENSION pDevExt;

    //创建设备名称
    UNICODE_STRING devName;
    RtlInitUnicodeString(
    &devName,L"\\Device\\MyDDKDevice");

    //创建设备
    status = IoCreateDevice( pDriverObject,
    sizeof(DEVICE_EXTENSION),
    &(UNICODE_STRING)devName,
    FILE_DEVICE_UNKNOWN,
    0, TRUE,
    &pDevObj );
    if (!NT_SUCCESS(status))
    return status;

    pDevObj
    ->Flags |= DO_BUFFERED_IO;
    pDevExt
    = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
    pDevExt
    ->pDevice = pDevObj;
    pDevExt
    ->ustrDeviceName = devName;

    pDevExt
    ->pIRPLinkListHead = (PLIST_ENTRY)ExAllocatePool(PagedPool,sizeof(LIST_ENTRY));
    InitializeListHead(pDevExt
    ->pIRPLinkListHead);

    //创建符号链接
    UNICODE_STRING symLinkName;
    RtlInitUnicodeString(
    &symLinkName,L"\\??\\HelloDDK");
    pDevExt
    ->ustrSymLinkName = symLinkName;
    status
    = IoCreateSymbolicLink( &symLinkName,&devName );
    if (!NT_SUCCESS(status))
    {
    IoDeleteDevice( pDevObj );
    return status;
    }
    return STATUS_SUCCESS;
    }

    /************************************************************************
    * 函数名称:HelloDDKUnload
    * 功能描述:负责驱动程序的卸载操作
    * 参数列表:
    pDriverObject:驱动对象
    * 返回 值:返回状态
    ************************************************************************
    */
    #pragma PAGEDCODE
    VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject)
    {
    PDEVICE_OBJECT pNextObj;
    KdPrint((
    "Enter DriverUnload\n"));
    pNextObj
    = pDriverObject->DeviceObject;
    while (pNextObj != NULL)
    {
    PDEVICE_EXTENSION pDevExt
    = (PDEVICE_EXTENSION)
    pNextObj
    ->DeviceExtension;

    //删除符号链接
    UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
    IoDeleteSymbolicLink(
    &pLinkName);

    ExFreePool(pDevExt
    ->pIRPLinkListHead);

    pNextObj
    = pNextObj->NextDevice;
    IoDeleteDevice( pDevExt
    ->pDevice );
    }
    }

    /************************************************************************
    * 函数名称:HelloDDKDispatchRoutin
    * 功能描述:对读IRP进行处理
    * 参数列表:
    pDevObj:功能设备对象
    pIrp:从IO请求包
    * 返回 值:返回状态
    ************************************************************************
    */
    #pragma PAGEDCODE
    NTSTATUS HelloDDKDispatchRoutin(IN PDEVICE_OBJECT pDevObj,
    IN PIRP pIrp)
    {
    KdPrint((
    "Enter HelloDDKDispatchRoutin\n"));

    PIO_STACK_LOCATION stack
    = IoGetCurrentIrpStackLocation(pIrp);
    //建立一个字符串数组与IRP类型对应起来
    static char* irpname[] =
    {
    "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_POWER",
    "IRP_MJ_SYSTEM_CONTROL",
    "IRP_MJ_DEVICE_CHANGE",
    "IRP_MJ_QUERY_QUOTA",
    "IRP_MJ_SET_QUOTA",
    "IRP_MJ_PNP",
    };

    UCHAR type
    = stack->MajorFunction;
    if (type >= arraysize(irpname))
    KdPrint((
    " - Unknown IRP, major type %X\n", type));
    else
    KdPrint((
    "\t%s\n", irpname[type]));


    //对一般IRP的简单操作,后面会介绍对IRP更复杂的操作
    NTSTATUS status = STATUS_SUCCESS;
    // 完成IRP
    pIrp->IoStatus.Status = status;
    pIrp
    ->IoStatus.Information = 0; // bytes xfered
    IoCompleteRequest( pIrp, IO_NO_INCREMENT );

    KdPrint((
    "Leave HelloDDKDispatchRoutin\n"));

    return status;
    }

    NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
    IN PIRP pIrp)
    {
    KdPrint((
    "Enter HelloDDKRead\n"));

    PDEVICE_EXTENSION pDevExt
    = (PDEVICE_EXTENSION)
    pDevObj
    ->DeviceExtension;

    PMY_IRP_ENTRY pIrp_entry
    = (PMY_IRP_ENTRY)ExAllocatePool(PagedPool,sizeof(MY_IRP_ENTRY));

    pIrp_entry
    ->pIRP = pIrp;

    //插入队列
    InsertHeadList(pDevExt->pIRPLinkListHead,&pIrp_entry->ListEntry);

    //将IRP设置为挂起
    IoMarkIrpPending(pIrp);

    KdPrint((
    "Leave HelloDDKRead\n"));

    //返回pending状态
    return STATUS_PENDING;
    }

    NTSTATUS HelloDDKCleanUp(IN PDEVICE_OBJECT pDevObj,
    IN PIRP pIrp)
    {
    KdPrint((
    "Enter HelloDDKCleanUp\n"));

    PDEVICE_EXTENSION pDevExt
    = (PDEVICE_EXTENSION)
    pDevObj
    ->DeviceExtension;

    //(1)将存在队列中的IRP逐个出队列,并处理

    PMY_IRP_ENTRY my_irp_entry;
    while(!IsListEmpty(pDevExt->pIRPLinkListHead))
    {
    PLIST_ENTRY pEntry
    = RemoveHeadList(pDevExt->pIRPLinkListHead);
    my_irp_entry
    = CONTAINING_RECORD(pEntry,
    MY_IRP_ENTRY,
    ListEntry);
    my_irp_entry
    ->pIRP->IoStatus.Status = STATUS_SUCCESS;
    my_irp_entry
    ->pIRP->IoStatus.Information = 0; // bytes xfered
    IoCompleteRequest( my_irp_entry->pIRP, IO_NO_INCREMENT );

    ExFreePool(my_irp_entry);
    }

    //(2)处理IRP_MJ_CLEANUP的IRP
    NTSTATUS status = STATUS_SUCCESS;
    // 完成IRP
    pIrp->IoStatus.Status = status;
    pIrp
    ->IoStatus.Information = 0; // bytes xfered
    IoCompleteRequest( pIrp, IO_NO_INCREMENT );

    KdPrint((
    "Leave HelloDDKCleanUp\n"));
    return STATUS_SUCCESS;
    }
    //应用程序
    #include <windows.h>
    #include
    <stdio.h>

    int main()
    {
    HANDLE hDevice
    =
    CreateFile(
    "\\\\.\\HelloDDK",
    GENERIC_READ
    | GENERIC_WRITE,
    0,
    NULL,
    OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL
    |FILE_FLAG_OVERLAPPED,//此处设置FILE_FLAG_OVERLAPPED
    NULL );

    if (hDevice == INVALID_HANDLE_VALUE)
    {
    printf(
    "Open Device failed!");
    return 1;
    }

    OVERLAPPED overlap1
    ={0};
    OVERLAPPED overlap2
    ={0};

    UCHAR buffer[
    10];
    ULONG ulRead;

    BOOL bRead
    = ReadFile(hDevice,buffer,10,&ulRead,&overlap1);
    if (!bRead && GetLastError()==ERROR_IO_PENDING)
    {
    printf(
    "The operation is pending\n");
    }
    bRead
    = ReadFile(hDevice,buffer,10,&ulRead,&overlap2);
    if (!bRead && GetLastError()==ERROR_IO_PENDING)
    {
    printf(
    "The operation is pending\n");
    }

    //迫使程序中止2秒
    Sleep(2000);

    //创建IRP_MJ_CLEANUP IRP
    CloseHandle(hDevice);

    return 0;
    }
    示例代码 P260

    取消IRP

    PDRIVER_CANCEL

      IoSetCancelRoutine(

        IN PIRP  Irp,

        IN PDRIVER_CANCEL  CancelRoutine

        );

    来设置取消IRP请求的回调函数。也可以用来删除取消例程,当CancelRoutine为空指针时,则删除原来设置的取消例程。

           IoCancelIrp函数指定取消IRP请求。在IoCancelIrp内部,通过cancel lock来进行同步。

    IoReleaseCancelSpinLock

    IoAcquireCancelSpinLock

           可以用CancelIo API 来取消IRP请求。在CancelIo内部会枚举所有没有被完成的IRP,依次调用IoCancelIrp。如果应用程序没有调用CancelIo,在应用程序关闭时也会自动调用CancelIo

           注意:cancel lock是全局自旋锁,占用时间不宜太长。

    代码
    1 VOID
    2 CancelReadIRP(
    3 IN PDEVICE_OBJECT DeviceObject,
    4 IN PIRP Irp
    5 )
    6 {
    7 KdPrint(("Enter CancelReadIRP\n"));
    8
    9 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
    10 DeviceObject->DeviceExtension;
    11
    12 //设置完成状态为STATUS_CANCELLED
    13 Irp->IoStatus.Status = STATUS_CANCELLED;
    14 Irp->IoStatus.Information = 0; // bytes xfered
    15 IoCompleteRequest( Irp, IO_NO_INCREMENT );
    16
    17 //释放Cancel自旋锁
    18 IoReleaseCancelSpinLock(Irp->CancelIrql);
    19
    20 KdPrint(("Leave CancelReadIRP\n"));
    21 }
    22
    23
    24 NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
    25 IN PIRP pIrp)
    26 {
    27 KdPrint(("Enter HelloDDKRead\n"));
    28
    29 PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
    30 pDevObj->DeviceExtension;
    31
    32 IoSetCancelRoutine(pIrp,CancelReadIRP);
    33
    34 //将IRP设置为挂起
    35 IoMarkIrpPending(pIrp);
    36
    37 KdPrint(("Leave HelloDDKRead\n"));
    38
    39 //返回pending状态
    40 return STATUS_PENDING;
    41 }
    42
    43

    代码
    1 #include <windows.h>
    2 #include <stdio.h>
    3
    4 int main()
    5 {
    6 HANDLE hDevice =
    7 CreateFile("\\\\.\\HelloDDK",
    8 GENERIC_READ | GENERIC_WRITE,
    9 0,
    10 NULL,
    11 OPEN_EXISTING,
    12 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,//此处设置FILE_FLAG_OVERLAPPED
    13 NULL );
    14
    15 if (hDevice == INVALID_HANDLE_VALUE)
    16 {
    17 printf("Open Device failed!");
    18 return 1;
    19 }
    20
    21 OVERLAPPED overlap1={0};
    22 OVERLAPPED overlap2={0};
    23
    24 UCHAR buffer[10];
    25 ULONG ulRead;
    26
    27 BOOL bRead = ReadFile(hDevice,buffer,10,&ulRead,&overlap1);
    28 if (!bRead && GetLastError()==ERROR_IO_PENDING)
    29 {
    30 printf("The operation is pending\n");
    31 }
    32 bRead = ReadFile(hDevice,buffer,10,&ulRead,&overlap2);
    33 if (!bRead && GetLastError()==ERROR_IO_PENDING)
    34 {
    35 printf("The operation is pending\n");
    36 }
    37
    38 //迫使程序中止2秒
    39 Sleep(2000);
    40
    41 //显式的调用CancelIo,其实在关闭设备时会自动运行CancelIo
    42 CancelIo(hDevice);
    43
    44 //创建IRP_MJ_CLEANUP IRP
    45 CloseHandle(hDevice);
    46
    47 return 0;
    48 }
    示例代码 P264

     

     

     

  • 相关阅读:
    Qt线程的简单使用(四)——QSemaphore的使用
    Qt线程的简单使用(三)——通过一个实例理解QMutex的使用
    Qt线程的简单使用(二)——通过继承QThread()创建线程
    Qt线程的简单使用(一)——通过QObject::moveToThread()创建线程
    高斯投影与UTM的异同
    基于Qt的Tcp协议的流程图
    OC系列高级-block
    OC系列foundation Kit基础-NSDate
    OC系列高级-内存管理关键字
    OC系列高级-NSValue
  • 原文地址:https://www.cnblogs.com/mydomain/p/1872222.html
Copyright © 2011-2022 走看看