對於裝置驅動程式通知應用程式的措施

  在目前流行的Windows作業系統中,裝置驅動程式是操縱硬體的最底層軟體介面。為了共享在裝置驅動程式設計過程中的經驗,給出裝置驅動程式通知應用程式的5種方法,具體說明每種方法的原理和實現過程,希望能夠給裝置驅動程式的設計者提供一些幫助。

  為了保證作業系統的平安性和穩定性以及應用程式的可移植性,Windows作業系統不答應應用程式直接訪問系統的硬體資源,而是必須藉助於相應的裝置驅動程式。裝置驅動程式可以直接操作硬體,假如應用程式和裝置驅動程式之間實現了雙向通訊,也就達到了應用程式控制底層硬體裝置的目的。它們之間的通訊包括兩個方面摘要:一方面是應用程式傳送給裝置驅動程式的資料;另一方面是裝置驅動程式傳送給應用程式的訊息。前者的實現較輕易,通過CreateFile函式獲取裝置驅動程式的控制代碼後,就可以使用Win32函式,如DeviceIoControl、ReadFile或WriteFile等實現應用程式和裝置驅動程式之間的通訊。後者的實現遠比前者複雜,同時介紹這方面情況的文章較少。這不等於說它不重要,相反,它在有些應用場合發揮著重要的功能。裝置驅動程式完成資料的採集工作後,需要馬上通知應用程式,以便應用程式能夠及時將資料取走並進行處理。諸如此類情況,不一而足。

  鑑於裝置驅動程式通知應用程式的重要性,本人結合一些經驗,對它進行了總結,歸納出5種方法摘要:非同步過程呼叫APC、事件方式VxD、訊息方式、非同步I/O方式和事件方式WDM。下面分別說明這幾種方式的原理,並給出實現的部分原始碼。

  1、 非同步過程呼叫APC

  Win32應用程式使用CreateFile函式動態載入裝置驅動程式,然後定義一個回撥函式backFunc,並且將回調函式的地址%26amp;backFunc作為引數,通過DeviceIoControl傳送給裝置驅動程式。裝置驅動程式獲得回撥函式的地址後,將它儲存在一個全域性變數如callback中,同時呼叫Get_Cur_Thread_Handle函式獲取它的應用程式執行緒的控制代碼,並且將該控制代碼儲存在一個全域性變數如appthread中。當條件成熟時,裝置驅動程式呼叫_VWIN32_QueueUserApc函式,向Win32應用程式傳送訊息。這個函式帶有三個引數摘要:第一個引數為回撥函式的地址已經註冊;第二個引數為傳遞給回撥函式的訊息;第三個引數為呼叫者的執行緒控制代碼已經註冊。Win32應用程式收到訊息後,自動呼叫回撥函式實際是由裝置驅動程式呼叫。回撥函式的輸入引數是由裝置驅動程式填入的,回撥函式在這裡主要是對訊息進行處理。

  2、 事件方式VxD

  首先,Win32應用程式建立一個事件的控制代碼,稱其為Ring3控制代碼。由於虛擬裝置驅動程式使用事件的Ring0控制代碼,因此,需要建立Ring0控制代碼。用LoadLibrary函式載入未公開的動態連結庫Kernel32.dll,獲得動態連結庫的控制代碼。然後,呼叫GetProcAddress, 找到函式OpenVxDHandle在動態連結庫中的位置。接著,用OpenVxDHandle函式將Ring3事件控制代碼轉化為Ring0事件控制代碼。Win32應用程式用CreateFile函式載入裝置驅動程式。假如載入成功,則呼叫DeviceIoControl函式將Ring0事件控制代碼傳給VxD;同時,建立一個輔助執行緒等待訊號變成有訊號狀態,本身則可去幹其它的事情。當條件成熟時,VxD置Ring0事件為有訊號狀態呼叫_VWIN32_SetWin32Event函式,這馬上觸發對應的Ring3事件為有訊號狀態。一旦Ring3事件控制代碼為有訊號狀態,Win32應用程式的輔助執行緒就對這個訊息進行相應的處理。

  3、 非同步I/O方式

  Win32應用程式首先呼叫CreateFile函式載入裝置驅動程式。在呼叫該函式時,將倒數第2個引數設定為FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,表示以後可以對檔案進行重疊I/O操作。當裝置驅動程式檔案建立成功後,建立一個初始態為無訊號、需要手動復位的事件,並且將這個事件傳給型別為OVERLAPPED的資料結構如Overlapped。然後,將Overlapped作為一個引數,傳給DeviceIoControl函式。裝置驅動程式把這個I/O請求包IRP設定為掛起狀態,並且設定一個取消例程。假如當前IRP佇列為空,則將這個IRP傳送給StartIo例程;否則,將它放到IRP佇列中。裝置驅動程式做完這些工作後,結束這個DeviceIoControl的處理,於是Win32應用程式可能不等待IRP處理完,就從DeviceIoControl的呼叫中返回。通過判定返回值,得到IRP的處理情況。假如當前IRP處於掛起狀態,則主程式先做一些其它的工作,然後呼叫WaitForSingleObject或WaitForMultipleObject函式等待Overlapped中的事件成為有訊號狀態。裝置驅動程式在適當的時候處理排隊的IRP,處理完成後,呼叫IoCompleteRequest函式。該函式將Overlapped中的事件設定為有訊號狀態。Win32應用程式對這個事件馬上進行響應,退出等待狀態,並且將事件復位為無訊號狀態,然後呼叫GetOverlappedResult函式獲取IRP的處理結果。

  4、 訊息方式

  Win32應用程式呼叫CreateFile函式動態載入虛擬裝置驅動程式。載入成功後,通過呼叫DeviceIoControl函式將窗體控制代碼傳送給VxD,VxD利用這個控制代碼向窗體發訊息。當條件滿足時,VxD呼叫SHELL_PostMessage函式向Win32應用程式傳送訊息。要讓該函式使用成功,必須用#define來自定義一個訊息,並且也要照樣在應用程式中定義它;還要在訊息迴圈中使用ON_MESSAGE來定義訊息對應的訊息處理函式,以便訊息產生時,能夠呼叫訊息處理函式。SHELL_PostMessage函式的第一個引數為Win32窗體控制代碼,第二個引數為訊息ID號,第三、四個引數為傳送給訊息處理函式的引數,第五、六個引數為回撥函式和傳給它的引數。Win32應用程式收到訊息後,對訊息進行處理。

  5、 事件方式WDM

  Win32應用程式首先建立一個事件,然後將該事件控制代碼傳給裝置驅動程式,接著建立一個輔助執行緒,等待事件的有訊號狀態,自己則接著幹其它事情。裝置驅動程式獲得該事件的控制代碼後,將它轉換成能夠使用的事件指標,並且把它寄存起來,以便後面使用。當條件具備後,裝置驅動程式將事件設定為有訊號狀態,這樣應用程式的輔助執行緒馬上知道這個訊息,於是進行相應的處理。當裝置驅動程式不再使用這個事件時,應該解除該事件的指標。

  6、 結語

  在目前流行的Windows作業系統中,裝置驅動程式是操縱硬體的最底層軟體介面。它向上提供和硬體無關的使用者介面,向下直接進行I/O、硬體中斷、DMA和記憶體訪問等操作。它將應用程式和硬體細節遮蔽開來,使軟體不依靠於硬體並且可在多個不同的平臺之間移植。本文介紹了5種裝置驅動程式通知應用程式的方法,其中前3種方法主要用於VxD中,後2種方法主要用於WDM。這5種方法都經過實際測試。測試結果表明,它們都能夠達到裝置驅動程式通知應用程式的目的。