針對延遲程序呼叫使用__sdv_save_request和__sdv_retrieve_request
延遲程式調用 (DPC) 對靜態驅動程式驗證器 (SDV) 提出挑戰,因為很難追蹤架構要求物件。 其中一個困難是要求必須從全域指標擷取,通常是從佇列內容,或從工作專案擷取。 為了克服這種困難,靜態驅動程式驗證器提供兩個函式: __sdv_save_request和 __sdv_retrieve_request。 這些函式會將延遲的要求對應至 SDV 可以追蹤的要求。
__sdv_save_request和__sdv_retrieve_request函式具有下列語法:
__sdv_save_request( request )
__sdv_retrieve_request( request )
其中 要求 可以是任何架構要求物件的控制碼。
這些函式僅供靜態分析工具使用。 編譯器會忽略函式。
下列程式碼範例示範 如何使用 __sdv_save_request 和 __sdv_retrieve_request 函式來引導 SDV,讓 SDV 可以對應延後的要求。 SDV 可以使用此對應來驗證 DeferredRequestCompleted 規則。 DeferredRequestCompleted 規則會要求 __sdv_save_request 和 __sdv_retrieve_request 出現在您的程式碼中。 有兩個驅動程式屬性規則 (AliasWithinDispatch、 AliasWithinTimerDpc) 尋找 __sdv_save_request 和 __sdv_retrieve_request 函式是否存在。
在下列程式碼範例中, EchoEvtIoRead 函式是 EvtIoRead 事件回呼函式,可將控制碼儲存至佇列內容區域中的架構要求物件。 EchoEvtTimerFunc函式是擷取它的EvtTimerFunc事件回呼函式。
VOID
EchoEvtIoRead(
)in WDFQUEUE Queue,
__in WDFREQUEST Request,
__in size_t Length
)
{
/* ..................... */
// Mark the request as cancelable
WdfRequestMarkCancelable(Request, EchoEvtRequestCancel);
// Defer the completion to another thread from the timer DPC and save the handle to the framework request object by using the __sdv_save_request function.
queueContext->CurrentRequest = Request;
__sdv_save_request(Request);
queueContext->CurrentStatus = Status;
return;
}
下列程式碼範例示範 __sdv_retrieve_request 函式如何對應現有的要求,讓 SDV 可以追蹤完成。
VOID
EchoEvtTimerFunc(
IN WDFTIMER Timer
)
{...................................................
queue = WdfTimerGetParentObject(Timer);
queueContext = QueueGetContext(queue);
//
// The DPC is automatically synchronized to the queue lock,
// so this prevents race conditions from occurring without explicit driver-managed locking. The __sdv_retrieve_request function is used so that SDV can restore the deferred request in the timer DPC. Because we know that this deferred request is valid (because it has been previously saved), the __analysis_assume function is used to suppress false defects that might otherwise result in this code path.
//
__sdv_retrieve_request(queueContext->CurrentRequest);
Request = queueContext->CurrentRequest;
__analysis_assume(Request != NULL);
if( Request != NULL ) {
//
// Try to remove cancel status from the request.
//
// The request is not completed if it is already canceled
// because the EchoEvtIoCancel function has run, or is about to run,
// and we are racing with it.
Status = WdfRequestUnmarkCancelable(Request);
// Because we know that the request is not NULL in this code path and that the request is no longer marked cancelable, we can use the __analysis_assume function to suppress the reporting of a false defect.
__analysis_assume(Status != STATUS_CANCELLED);
if( Status != STATUS_CANCELLED ) {
queueContext->CurrentRequest = NULL;
Status = queueContext->CurrentStatus;
KdPrint(("CustomTimerDPC Completing request 0x%p, Status 0x%x \n", Request,Status));
WdfRequestComplete(Request, Status);
}
else {
KdPrint(("CustomTimerDPC Request 0x%p is STATUS_CANCELLED, not completing\n",
Request));
}
}
return;
}