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

来源:互联网 发布:thinkphp开发实例源码 编辑:程序博客网 时间:2024/06/08 07:02

一直所做的都是同步实现的。当然很多情况这并不是很好的解决问题。现在手上的问题是:用户层通知底层驱动(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
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 oppo手机屏幕锁忘了怎么办 三星手机屏幕锁忘了怎么办 手机主菜单坏了怎么办 索尼手机密码忘了怎么办 索尼笔记本密码忘了怎么办 索尼手机解锁密码忘了怎么办 索尼记录仪密码忘了怎么办 索尼z3手机忘记开机密码怎么办 索尼忘记锁屏密码怎么办 索尼手机忘记锁屏密码怎么办 忘了手机解锁图案怎么办 索尼手机忘记开机密码怎么办 索尼笔记本开机密码忘了怎么办 手机的开机密码忘了怎么办 联想手机开机密码忘了怎么办 红米note3忘记开机密码怎么办 小米2忘了密码怎么办 小米笔记本电脑忘记开机密码怎么办 小米笔记本忘记开机密码怎么办 小米手机儿童模式忘记密码怎么办 小米应用锁密码忘了怎么办 小米air密码忘了怎么办 小米4密码忘了怎么办 小米手机开机密码忘了怎么办? 小米笔记本电脑开机密码忘了怎么办 小米笔记本开机密码忘了怎么办 htc手机忘记解锁图案怎么办 红米手机屏幕锁定怎么解锁怎么办 小米5s有id怎么办 手机密码找不回来了怎么办? 手机密码图案忘了怎么办 手机屏幕图案锁忘了怎么办 捡到苹果7有id锁怎么办 魅族什么都忘了怎么办 海信电视百事通登陆失败怎么办 去哪儿换号了怎么办 ipan充不进去电怎么办 安卓数据线松了怎么办 索尼z5耳机掉漆怎么办 索尼z5无限重启怎么办 苹果8基带坏了怎么办