下面是開發人員在使用 Windows NT 裝置驅動程式時應當避免的事項列表:
1. 一定不要在沒有標註 I/O 請求資料包 (IRP) 掛起 (IoMarkIrpPending) 的情況下通過調度常式返回 STATUS_PENDING。
2. 一定不要通過插斷服務常式 (ISR) 調用 KeSynchronizeExecution。 它會使系統死結。
3. 一定不要將 DeviceObject->Flags 設定為 DO_BUFFERED_IO 和 DO_DIRECT_IO。 它會擾亂系統並最終導致致命錯誤。 而且,一定不要在 DeviceObject->Flags 中設定 METHOD_BUFFERED、METHOD_NEITHER、METHOD_IN_DIRECT 或 METHOD_OUT_DIRECT,因為這些值只在定義 IOCTL 時使用。
4. 一定不要通過頁面緩衝池分配發送器對象。 如果這樣做,將會偶爾導致系統故障檢測 (Bugcheck)。
5. 當運行於 IRQL >= DISPATCH_LEVEL 時,一定不要通過頁面緩衝池分配記憶體,或訪問頁面緩衝池中的記憶體。 這是一個致命錯誤。
6. 一定不要在 IRQL >= DISPATCH_LEVEL 上等候核心發送器對象出現非零間隔。 這是一個致命錯誤。
7. 在 IRQL >= DISPATCH_LEVEL 上執行時,一定不要調用任何導致調用線程發生直接或間接等待的函數。 這是一個致命錯誤。
8. 一定不要把插斷要求層級 (IRQL) 降低到低於您的頂級常式被調用的層級。
9. 如果沒有調用過 KeRaiseIrql(),則一定不要調用 KeLowerIrql()。
10. 一定不要使處理器 (KeStallExecutionProcessor) 停止運轉的時間超過 50 微秒。
11. 一定不要使旋轉鎖 (Spin Lock) 保持鎖定狀態的時間超過您的需要。 要使系統獲得更好的總體效能,請不要使任何系統範圍內有效旋轉鎖的鎖定時間超過 25 微秒。
12. 當 IRQL 大於 DISPATCH_LEVEL 時,一定不要調用 KeAcquireSpinLock 和 KeReleaseSpinLock,或 KeAcquireSpinLockAtDpcLevel 和 KeReleaseSpinLockFromDpcLevel。
13. 一定不要通過調用 KeReleaseSpinLockFromDpcLevel 來釋放 KeAcquireSpinLock 所擷取的旋轉鎖,因為這會使原始 IRQL 無法被還原。
14. 一定不要在 ISR 或 SynchCritSection 常式中調用 KeAcquireSpinLock 和 KeReleaseSpinLock 或者其它任何使用可執行旋轉鎖的常式。
15. 當您在常式中而不是在 DriverEntry 中建立裝置對象時,一定不要忘記清除 DO_DEVICE_INITIALIZING 標記。
16. 一定不要同時在不同處理器的多個線程中將延時程序呼叫 (DPC) 對象添加到隊列中(使用 KeInsertQueueDpc)。 這會導致致命錯誤。
17. 一定不要通過 CutomerTimerDPC 常式釋放周期定時器。 您可以通過 DPC 常式釋放非周期定時器。
18. 一定不要將相同的 DPC 指標傳遞給 KeSetTimer,或者 KeSetTimerEx (CustomTimerDpc) 和 KeInsertQueueDpc (CustomDpc),因為這將導致競爭。
19. 旋轉鎖鎖定時,一定不要調用 IoStartNextPacket。 這將使系統死結。
20. 旋轉鎖鎖定時,一定不要調用 IoCompleteRequest。 這將使系統死結。
21. 如果您的驅動程式設定了完成常式,那麼一定不要在沒有把完成常式設定為 NULL 的情況下調用 IoCompleteRequest。
22. 調用 IoCompleteRequest 之前,一定不要忘記設定 IRP 中的 I/O 狀態區。
23. 在將 IRP 添加到隊列中或將它發送到另一個驅動程式 (IoCallDriver) 之後,一定不要調用 IoMarkPending。 在驅動程式調用 IoMarkPending 之前,IRP 可能已經完成,由此可能發生故障檢測。 對於包含完成常式的驅動程式,如果設定了 Irp->PendingReturned,則完成常式必須調用 IoMarkPending。
24. 一定不要在已經對某個 IRP 調用 IoCompleteRequest 之後再去訪問該 IRP。
25. 一定不要對不屬於您的驅動程式的 IRP 調用 IoCancelIrp,除非您知道該 IRP 還沒有完成。
26. 在您的調度常式返回到調用者之前,一定不要對您的調度常式正在處理的 IRP 調
55. 一定不要在任意的線程上下文 中使用 IoBuildSynchronousFsdRequest/IoBuildDeviceIoControlRequest 來分配 IRP,因為該 IRP 依然與該線程保持關聯 (Irp->ThreadListEntry),直到它被釋放。
56. 如果已經使用 IoAllocateIrp 在 ChargeQuota 參數被設定為 TRUE 的情況下分配了某個 IRP,那麼一定不要對該 IRP 調用 IoInitializeIrp。 如果在 ChargeQuota 設定為 TRUE 的情況下分配 IRP,則 I/O 管理器將把它為該 IRP 分配記憶體時所用的緩衝池的相關資訊儲存在該 IRP 的內部標記中。
如果對這樣的 IRP 調用 IoInitializeIrp,那麼,當該函數盲目地清零整個 IRP 時,分配池資訊將會丟失。 當您釋放 IRP 時,這將導致記憶體被破壞。 同時,一定不要重用來自 IO 管理器的 IRP。 如果要重用 IRP,應當使用 IoAllocateIrp 分配您自己的 IRP。
57. 如果在調用線程的堆棧中分配 了對象,就一定不要在 KeWaitForSingleObject/KeWaitForMultipleObjects 中將 WaitMode 指定為 UserMode。 這樣做的結果是,如果被等候的對象是在函數堆棧中建立的,那麼您必須將 WaitMode 指定為 KernelMode 才能防止線程被頁面調度出去。
58. 在沒有對關鍵節中的代碼加以保護的情況下,一定不要在使用者模式線程的上下文中擷取諸如 ERESOURCES 和 FastMutex(Unsafe) 這類資源。
因為擷取這些資源不會使 IRQL 提高到 APC_LEVEL,所以,如果線程在已擷取資源後被掛起(通過將 APC 排入佇列實現),它可能導致死結,並使系統安全性降低。 因此,應當通過顯式地將 IRQL 提高到 APC_LEVEL,或者調用 KeEnterCriticalRegion 來進入關鍵段,然後可以擷取這些資源。