一直所做的都是同步实现的。当然很多情况这并不是很好的解决问题。现在手上的问题是:用户层通知底层驱动(Filter Driver)做某件事,然后返回该事件执行的结果。如果该事件是一件简单的事情,这里是指极短时间内可以完成的,那么在允许范围内,我们可以用同步来完成。但是如果该事件是一件耗时的工作,而应用程序不能一直在等着该事件的完成信号,况且好像DeviceIoControl有时间限制的(?)。这就需要用异步的方式来解决问题:例如:同事叫你去吃饭,你听到后,可以马上去,也可以等会再去,吃完后再回到Office就好了。关键是我以前没有实现过,现在就手上的数资料来分析下可以实现的流程。
一、我们先看看关键函数DeviceIoControl:
BOOL WINAPI DeviceIoControl( __in HANDLE hDevice, __in DWORD dwIoControlCode, __in_opt LPVOID lpInBuffer, __in DWORD nInBufferSize, __out_opt LPVOID lpOutBuffer, __in DWORD nOutBufferSize, __out_opt LPDWORD lpBytesReturned, __inout_opt LPOVERLAPPED lpOverlapped );
+ ===== lpOverlapped =====
+ 一个指向OVERLAPPED结构体的指针 。
+ 如果hDevice用FILE_FLAG_OVERLAPPED 形式打开,lpOverlapped 必须指向一个合法的OVERLAPPED结构体。在这种情况下,进行异步操作 。
+ 如果hDevice用FILE_FLAG_OVERLAPPED 形式打开,而lpOverlapped为NULL,函数会返回不可预知的错误。
+ 如果hDevice打开时没有指定FILE_FLAG_OVERLAPPED 标志,lpOverlapped参数将被忽略,进行同步操作,函数直到操作完成或出现错误时才返回。
所以这里我们必须首先以FILE_FLAG_OVERLAPPED打开设备驱动。这里我们需要在CreateFile中指定:
HANDLE WINAPI CreateFile( __in LPCTSTR lpFileName, __in DWORD dwDesiredAccess, __in DWORD dwShareMode, __in_opt LPSECURITY_ATTRIBUTES lpSecurityAttributes, __in DWORD dwCreationDisposition, __in DWORD dwFlagsAndAttributes, __in_opt HANDLE hTemplateFile );
如果dwFlagsAndAttributes指定值为:FILE_FLAG_OVERLAPPED,那么
现在的问题来了,我们Overlapped的方式打开设备驱动,然后以异步的方式调用了DeviceIoControl,所以该函数会立马返回,返回值正确的应该为:ERROR_IO_PENDING 。这就表明底层驱动接受到了请求,然后应用程序应该有一种方式可以检测到该请求被底层正确执行完成的信号。
这里有个疑问:网上看到很多的例子,都是手动触发异步的完成,包括:驱动和应用层的异步通信
他们的做法没有等到最后的执行结果返回:所以在底层驱动手动设置了一些信息:
//获取irp状态 pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = STATUS_SUCCESS; pIrpStack = IoGetCurrentIrpStackLocation(pIrp); IoCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode; switch (IoCode) { case IO_TEST_PENDING: status = PsCreateSystemThread(&hthread, (ACCESS_MASK)0L, NULL, (HANDLE)0, NULL, ThreadPending, NULL); if (!NT_SUCCESS(status)) { return status; } KdPrint(("Pending thread ok...")); //直接设置为pending 返回给应用层。 status = STATUS_PENDING; IoMarkIrpPending(pIrp); pIrp->IoStatus.Status = status; return status; break; }
可能他们仅仅是为了测试,没有叫底层驱动做一些复杂的事情。接着讲...
二、等待执行完成信号
这里调用WaitForSingleObject并传递设备内核对象的句柄。
WaitForSingleObject会挂起调用线程直至内核对象变成有信号态。
DWORD WINAPI WaitForSingleObject( __in HANDLE hHandle, __in DWORD dwMilliseconds );
WaitForSingleObject 函数用来检测 hHandle 事件的信号状态,当函数的执行时间超过 dwMilliseconds 就返回,但如果参数 dwMilliseconds 为 INFINITE 时函数将直到相应时间事件变成有信号状态才返回,否则就一直等待下去,直到 WaitForSingleObject 有返回直才执行后面的代码。
这里我们把它处理成一个Event,所以在调用DeviceIoControl之前,我们必须创建一个Event:
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性 BOOL bManualReset, // 复位方式 BOOL bInitialState, // 初始状态 LPCTSTR lpName // 对象名称 );
示例: // 创建一个有名的,不能被继承的,手动复原,初始状态是无信号状态的事件对象
Handle h = CreateEvent(NULL,TRUE,FALSE,“MyEvent”);
然后作为OVERLAPPED数据成员传入DeviceIoControl函数。
例如:
OVERLAPPED varHIDOverlapped; .. varEventObjectHandle = CreateEvent(NULL, TRUE, TRUE, ""); if(varEventObjectHandle == INVALID_HANDLE_VALUE || varEventObjectHandle == NULL){ ..} varHIDOverlapped.hEvent = varEventObjectHandle; varHIDOverlapped.Offset = 0; varHIDOverlapped.OffsetHigh = 0; varCommand[0] = 0x05; varCommand[1] = 0; varCommand[2] = 0; DeviceIoControl (varUSBHandle, IOCTL_USBPRINT_VENDOR_GET_COMMAND, varCommand, 3, varStatus, 31, (LPDWORD)&varNumberOfBytes, (LPOVERLAPPED) &varHIDOverlapped); varEventResult = WaitForSingleObject(varEventObjectHandle, 2000);
当varEventResult返回的结果是:WAIT_OBJECT_0。表明句柄是一个signaled状态,然后应用线程接着执行余下的代码:
switch (varEventResult) { case WAIT_OBJECT_0: // It works break; case WAIT_TIMEOUT: // Timeout varEventResult = CancelIo(varUSBHandle); break; default: break; }
当然你也可是这样使用:
// This will return immediately... ULONG rc = DeviceIoControl( handle_, IOCTL_TSII_BOARD_SIGNAL_AT_TIMECODE, &tcsp, sizeof(tcsp), NULL, NULL, &nbytes, &overlapped); // How do I handle this??? if (rc == 0){ if (GetLastError() != ERROR_IO_PENDING) { throw Exception("overlapped i/o exception\n"); } } DWORD transf_byte; if(GetOverlappedResult(handle_,&overlapped,&transf_byte,TRUE) == 0) { //ERROR }
关于GetOverlappedResult:
BOOL WINAPI GetOverlappedResult( HANDLE hFile, LPOVERLAPPED lpOverlapped, LPDWORD lpNumberOfBytesTransferred, BOOL bWait )
The GetOverlappedResult function returns the result of the last
operation that used lpOverlapped and returned ERROR_IO_PENDING.
这样应该就差不多可以了吧(我猜的~,细节除外)。
下面是参考资料: