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

     

     

     

  • 相关阅读:
    算法训练 P1103
    算法训练 表达式计算
    算法训练 表达式计算
    基础练习 时间转换
    基础练习 字符串对比
    Codeforces 527D Clique Problem
    Codeforces 527C Glass Carving
    Codeforces 527B Error Correct System
    Codeforces 527A Glass Carving
    Topcoder SRM 655 DIV1 250 CountryGroupHard
  • 原文地址:https://www.cnblogs.com/mydomain/p/1872222.html
Copyright © 2011-2022 走看看