Windows檔案系統過濾驅動開發教程
注: 有任何問題與建議請加QQ16191935,郵箱MFC_Tan_Wen@163.com
工作忙,好久沒有來過了,請大家諒解。
10 自己發送Irp完成讀請求
關於這個有一篇文檔解釋得很詳細,不過我認為樣本的代碼有點太簡略了,這篇文檔在IFS所附帶的OSR文檔中,請自己尋找。
為何要自己發送Irp?在一個檔案過濾驅動中,如果你打算讀寫檔案,可以試用ZwReadFile.但是這有一些問題。Zw系列的Native API使
用控制代碼。而控制代碼是有線程環境限制的。此外也有中斷層級的限制。再說,Zw系列函數來讀寫檔案,最終還是要發出Irp,又會被自己的過濾驅動
捕獲到。結果帶來判斷上的困難。對資源也是浪費。那麼最應該的辦法是什麼呢?當然是直接對卷裝置發Irp了。
但是Irp是非常複雜的資料結構,而且又被微軟所構造的很多未空開的組件所處理。所以自己發irp並不是一件簡單的事情。
比較萬能的方法是IoAllocateIrp,分配後自己逐個填寫。問題是細節實在太多,很多無文檔可尋。有興趣的應該看看我上邊所提及的
那篇文章“Rolling Your Own”。
有意思的是這篇文章後來提到了捷徑,就是利用三個函數:
IoBuildAsynchronousFsdRequest(...)
IoBuildSynchronousFsdRequest(...)
IoBuildDeviceIoControlRequest(...)
於是我參考了他這方面的範例程式碼,發現運行良好,程式也很簡單。建議怕深入研究的選手就可以使用我下邊提供的方法了。
首先的建議是使用IoBuildAsynchronousFsdRequest(),而不要使用同步的那個。使用非同步Irp使irp和線程無關。而你的過濾驅動一
般很難把握當前線程(如果你開一個系統線程來專門讀取檔案那例外)。此時,你可以輕鬆的在Irp的完成函數中刪除你分配過的Irp,避免去追
究和線程相關的事情。
但是這個方法有局限性。文檔指出,這個方法僅僅能用於IRP_MJ_READ,IRP_MJ_WRITE,IRP_MJ_FLUSH_BUFFERS,和IRP_MJ_SHUTDOWN.
剛好我這裡僅僅要求完成檔案讀。
用Irp完成檔案讀需要一個FILE_OBJECT.FileObject是比Zw系列所用的控制代碼更好的東西。因為這個FileObject是和線程無關的。你可以
放心的在未知的線程中使用他。
自己要獲得一個FILE_OBJECT必須自己發送IRP_MJ_CREATE的IRP.這又不是一件輕鬆的事情。不過我跳過了這個問題。因為我是檔案系
統過濾驅動,所以我從上面發來的IRP中得到FILE_OBJECT,然後再構造自己的IRP使用這個FILE_OBJECT,我發現運行很好。
但是又出現一個問題,如果IRP的irp->Flags中有IRP_PAGING(或者說是Cache管理器所發來的IRP)標記,則其FileObject我獲得並使
用後,老是返回錯誤。閱讀網上的經驗表明,帶有IRP_PAGINGE的FileObject不可以使用.於是我避免使用這時的FileObject.我總是使用不帶
IRP_PAGING的Irp(認為是使用者程式發來的讀請求)的FileObject。
好,現在廢話很多了,現在來看看構造irp的代碼:
_inline wd_irp *wd_irp_fsd_read_alloc(wd_dev *dev,
wd_void *buf,
wd_ulong length,
wd_lgint *offset,
wd_io_stat_block *io_stat)
{
return IoBuildAsynchronousFsdRequest(IRP_MJ_READ,dev,
buf,length,
offset,
io_stat);
}
io_stat我不知道是做何用,我一般填寫NULL.類型是PIO_STATUS_BLOCK.buf則是緩衝。在Irp中被用做UserBuffer接收資料。offset是
這次讀的位移量。
以上函數構造一個讀irp.請注意,此時您還沒有設定FileObject.實際上我是這樣發出請求的:
irp = wd_irp_fsd_read_alloc(dev,buf,len,&start,NULL);
if(irp == NULL)
return;
irpsp = wd_irp_next_sp(irp);
wd_irpsp_file_set(irpsp,file);
wd_irp_comp(irp,my_req_comp,context);
請注意wd_irp_next_sp,我是得到了這個irp的下一個IO_STACK_LOCATION,然後我設定了FileObject.接下來應該設定了完成後,我就
可以發送請求了!請求發送完畢後,一旦系統完成請求就會調用你的my_req_comp.
再看看my_req_comp如何收場:
wd_stat my_req_comp(in wd_dev *dev,
in wd_irp *irp,
in wd_void *context)
{
// 請注意,無論成功還是失敗,我都得在這裡徹底銷毀irp
wd_irp_send_by_myself_free(irp);
// 返回這個,僅僅是因為irp我已經銷毀,不想讓系統再次銷毀它而已。
return wd_stat_more_processing;
}
wd_stat_more_prcessing就是STATUS_MORE_PROCESSING_REQUIRED。之所以返回這個,是因為我已經把irp給刪除了。我不想windows系
統再對這個irp做什麼。所以乾脆說我還要繼續處理,這樣避免了io管理器再去動用這個irp的可能。
最後是那個irp刪除函數的代碼:
_inline wd_void wd_irp_send_by_myself_free(wd_irp *irp)
{
if (irp->MdlAddress)
{
MmUnmapLockedPages(MmGetSystemAddressForMdl(irp->MdlAddress),
irp->MdlAddress);
MmUnlockPages(irp->MdlAddress);
IoFreeMdl(irp->MdlAddress);
}
IoFreeIrp(irp);
};
因為我自己沒有分配過MdlAddress下去過。所以如果下邊返回了MDL,我得附帶清理它。
好了,祝願你心情愉快。如果你何我一樣懶惰的話,不妨就用上邊的簡單方法自己發irp來讀檔案了。