关于DeviceIoControl实现异步的笔记【1】

转:http://yexin218.iteye.com/blog/638445

一直所做的都是同步实现的。当然很多情况这并不是很好的解决问题。现在手上的问题是:用户层通知底层驱动(Filter Driver)做某件事,然后返回该事件执行的结果。如果该事件是一件简单的事情,这里是指极短时间内可以完成的,那么在允许范围内,我们可以用同步来完成。但是如果该事件是一件耗时的工作,而应用程序不能一直在等着该事件的完成信号,况且好像DeviceIoControl有时间限制的(?)。这就需要用异步的方式来解决问题:例如:同事叫你去吃饭,你听到后,可以马上去,也可以等会再去,吃完后再回到Office就好了。关键是我以前没有实现过,现在就手上的数资料来分析下可以实现的流程。

一、我们先看看关键函数DeviceIoControl:

Cpp代码 复制代码  收藏代码
  1. BOOL WINAPI DeviceIoControl(   
  2.   __in         HANDLE hDevice,   
  3.   __in         DWORD dwIoControlCode,   
  4.   __in_opt     LPVOID lpInBuffer,   
  5.   __in         DWORD nInBufferSize,   
  6.   __out_opt    LPVOID lpOutBuffer,   
  7.   __in         DWORD nOutBufferSize,   
  8.   __out_opt    LPDWORD lpBytesReturned,   
  9.   __inout_opt  LPOVERLAPPED lpOverlapped   
  10. );  

+ ===== lpOverlapped ===== 
+ 一个指向OVERLAPPED结构体的指针
+ 如果hDevice用FILE_FLAG_OVERLAPPED 形式打开,lpOverlapped 必须指向一个合法的OVERLAPPED结构体。在这种情况下,进行异步操作
+ 如果hDevice用FILE_FLAG_OVERLAPPED 形式打开,而lpOverlapped为NULL,函数会返回不可预知的错误。
+ 如果hDevice打开时没有指定FILE_FLAG_OVERLAPPED 标志,lpOverlapped参数将被忽略,进行同步操作,函数直到操作完成或出现错误时才返回。

所以这里我们必须首先以FILE_FLAG_OVERLAPPED打开设备驱动。这里我们需要在CreateFile中指定:

Cpp代码 复制代码  收藏代码
  1. HANDLE WINAPI CreateFile(   
  2.   __in      LPCTSTR lpFileName,   
  3.   __in      DWORD dwDesiredAccess,   
  4.   __in      DWORD dwShareMode,   
  5.   __in_opt  LPSECURITY_ATTRIBUTES lpSecurityAttributes,   
  6.   __in      DWORD dwCreationDisposition,   
  7.   __in      DWORD dwFlagsAndAttributes,   
  8.   __in_opt  HANDLE hTemplateFile   
  9. );  

 如果dwFlagsAndAttributes指定值为:FILE_FLAG_OVERLAPPED,那么

写道
The file or device is being opened or created for asynchronous I/O.

When subsequent I/O operations are completed on this handle, the event specified in the OVERLAPPED structure will be set to the signaled state.

If this flag is specified, the file can be used for simultaneous read and write operations.

If this flag is not specified, then I/O operations are serialized, even if the calls to the read and write functions specify an OVERLAPPED structure.

For information about considerations when using a file handle created with this flag, see the Synchronous and Asynchronous I/O Handles section of this topic.

 

现在的问题来了,我们Overlapped的方式打开设备驱动,然后以异步的方式调用了DeviceIoControl,所以该函数会立马返回,返回值正确的应该为:ERROR_IO_PENDING 。这就表明底层驱动接受到了请求,然后应用程序应该有一种方式可以检测到该请求被底层正确执行完成的信号。

这里有个疑问:网上看到很多的例子,都是手动触发异步的完成,包括:驱动和应用层的异步通信

他们的做法没有等到最后的执行结果返回:所以在底层驱动手动设置了一些信息:

Cpp代码 复制代码  收藏代码
  1. //获取irp状态   
  2.     pIrp->IoStatus.Information = 0;   
  3.     pIrp->IoStatus.Status = STATUS_SUCCESS;   
  4.   
  5.     pIrpStack = IoGetCurrentIrpStackLocation(pIrp);   
  6.     IoCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode;   
  7.         switch (IoCode)   
  8.     {   
  9.     case IO_TEST_PENDING:   
  10.         status = PsCreateSystemThread(&hthread, (ACCESS_MASK)0L, NULL, (HANDLE)0, NULL, ThreadPending, NULL);   
  11.         if (!NT_SUCCESS(status))   
  12.         {   
  13.             return status;   
  14.         }   
  15.            
  16.         KdPrint(("Pending thread ok..."));   
  17.   
  18.     //直接设置为pending 返回给应用层。   
  19.         status = STATUS_PENDING;   
  20.         IoMarkIrpPending(pIrp);   
  21.         pIrp->IoStatus.Status = status;   
  22.         return status;   
  23.         break;       
  24.     }  

 

可能他们仅仅是为了测试,没有叫底层驱动做一些复杂的事情。接着讲...

二、等待执行完成信号

这里调用WaitForSingleObject并传递设备内核对象的句柄。

WaitForSingleObject会挂起调用线程直至内核对象变成有信号态。 

Cpp代码 复制代码  收藏代码
  1. DWORD WINAPI WaitForSingleObject(   
  2.   __in  HANDLE hHandle,   
  3.   __in  DWORD dwMilliseconds   
  4. );  

 

WaitForSingleObject 函数用来检测 hHandle 事件的信号状态,当函数的执行时间超过 dwMilliseconds 就返回,但如果参数 dwMilliseconds INFINITE 时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到 WaitForSingleObject 有返回直才执行后面的代码。

这里我们把它处理成一个Event,所以在调用DeviceIoControl之前,我们必须创建一个Event:

 

Cpp代码 复制代码  收藏代码
  1. HANDLE CreateEvent(      
  2.   LPSECURITY_ATTRIBUTES lpEventAttributes,   // 安全属性      
  3.   BOOL bManualReset,   // 复位方式      
  4.   BOOL bInitialState,   // 初始状态      
  5.   LPCTSTR lpName   // 对象名称      
  6. );    

 示例:  // 创建一个有名的,不能被继承的,手动复原,初始状态是无信号状态的事件对象
          Handle h = CreateEvent(NULL,TRUE,FALSE,“MyEvent”);  

然后作为OVERLAPPED数据成员传入DeviceIoControl函数。

例如:

Cpp代码 复制代码  收藏代码
  1. OVERLAPPED varHIDOverlapped;   
  2. ..   
  3. varEventObjectHandle = CreateEvent(NULL, TRUE, TRUE, "");   
  4.   
  5. if(varEventObjectHandle == INVALID_HANDLE_VALUE || varEventObjectHandle   
  6. == NULL){   
  7.   
  8. ..}   
  9.   
  10. varHIDOverlapped.hEvent = varEventObjectHandle;   
  11. varHIDOverlapped.Offset = 0;   
  12. varHIDOverlapped.OffsetHigh = 0;   
  13.   
  14. varCommand[0] = 0x05;   
  15. varCommand[1] = 0;   
  16. varCommand[2] = 0;   
  17. DeviceIoControl (varUSBHandle, IOCTL_USBPRINT_VENDOR_GET_COMMAND,   
  18. varCommand, 3, varStatus, 31, (LPDWORD)&varNumberOfBytes, (LPOVERLAPPED)   
  19. &varHIDOverlapped);   
  20.   
  21. varEventResult = WaitForSingleObject(varEventObjectHandle, 2000);  

 

当varEventResult返回的结果是:WAIT_OBJECT_0。表明句柄是一个signaled状态,然后应用线程接着执行余下的代码:

Cpp代码 复制代码  收藏代码
  1. switch (varEventResult)   
  2. {   
  3. case WAIT_OBJECT_0:   
  4. // It works   
  5. break;   
  6.   
  7. case WAIT_TIMEOUT:   
  8. // Timeout   
  9. varEventResult = CancelIo(varUSBHandle);   
  10. break;   
  11.   
  12. default:   
  13. break;   
  14. }  

 

当然你也可是这样使用:

Cpp代码 复制代码  收藏代码
  1.  // This will return immediately...    
  2. ULONG rc = DeviceIoControl( handle_, IOCTL_TSII_BOARD_SIGNAL_AT_TIMECODE, &tcsp,    
  3. sizeof(tcsp), NULL, NULL, &nbytes, &overlapped); // How do I handle this???   
  4.  if (rc == 0){    
  5.      if (GetLastError() != ERROR_IO_PENDING) {    
  6.                throw Exception("overlapped i/o exception\n");   
  7.         
  8.       }   
  9.     }    
  10. DWORD  transf_byte;   
  11.  if(GetOverlappedResult(handle_,&overlapped,&transf_byte,TRUE) == 0)   
  12. //ERROR   
  13.   
  14. }  

 

关于GetOverlappedResult:

Cpp代码 复制代码  收藏代码
  1. BOOL  
  2. WINAPI   
  3. GetOverlappedResult(   
  4.     HANDLE hFile,   
  5.     LPOVERLAPPED lpOverlapped,   
  6.     LPDWORD lpNumberOfBytesTransferred,   
  7.     BOOL bWait   
  8.     )  

 

  The GetOverlappedResult function returns the result of the last
    operation that used lpOverlapped and returned ERROR_IO_PENDING.

这样应该就差不多可以了吧(我猜的~,细节除外)。

下面是参考资料:

  1. 驱动和应用层的异步通信 http://bbs.pediy.com/showthread.php?t=59015
  2. DeviceIoControl的异步问题http://bbs.driverdevelop.com/read.php?tid-67288.html
  3. WaitForSingleObject的用法http://hi.baidu.com/zouhaoo/blog/item/1e863851615e3b858d54306c.html
  4. 多线程中使用waitforsingleobject方法http://www.360doc.com/content/09/0428/12/27287_3299491.shtml
  5. DeviceIoControl return code using Overlapped I/O http://www.osronline.com/showthread.cfm?link=167510
  6. 应用层跟驱动异步通信的问题,irp该如何处理?http://bbs.driverdevelop.com/read.php?tid-113399.html
  7. DeviceIOControl and overlapped I/O problem http://forums.devshed.com/c-programming-42/deviceiocontrol-and-overlapped-i-o-problem-255708.html
  8. http://www.techtalkz.com/microsoft-device-drivers/295657-deviceiocontrol-overlapped.html
  9. DeviceIoControl and OVERLAPPED problem

你可能感兴趣的:(关于DeviceIoControl实现异步的笔记【1】)