關於DeviceIoControl實現異步的筆記【1】

一直所做的都是同步實現的。當然很多情況這並不是很好的解決問題。現在手上的問題是:用戶層通知底層驅動(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,那麼

寫道
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 。這就表明底層驅動接受到了請求,然後應用程序應該有一種方式可以檢測到該請求被底層正確執行完成的信號。

這裏有個疑問:網上看到很多的例子,都是手動觸發異步的完成,包括:驅動和應用層的異步通信

他們的做法沒有等到最後的執行結果返回:所以在底層驅動手動設置了一些信息:

//獲取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.

這樣應該就差不多可以了吧(我猜的~,細節除外)。

下面是參考資料:

  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
發佈了53 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章