Freigeben über


Verwenden von __sdv_save_request und __sdv_retrieve_request für verzögerte Prozeduraufrufe

Verzögerte Prozeduraufrufe (DpCs) stellen Herausforderungen für die Statische Treiberüberprüfung (Static Driver Verifier, SDV) dar, da es schwierig ist, das Frameworkanforderungsobjekt nachzuverfolgen. Eine Schwierigkeit besteht darin, dass die Anforderung aus einem globalen Zeiger abgerufen werden muss, in der Regel aus dem Warteschlangenkontext oder aus einem Arbeitselement. Um diese Schwierigkeit zu überwinden, bietet static Driver Verifier zwei Funktionen: __sdv_save_request und __sdv_retrieve_request. Diese Funktionen ordnen die verzögerte Anforderung einer Anforderung zu, die SDV nachverfolgen kann.

Die Funktionen __sdv_save_request und __sdv_retrieve_request weisen die folgende Syntax auf:

__sdv_save_request( request ) 
__sdv_retrieve_request( request ) 

Dabei kann die Anforderung ein Handle für ein beliebiges Frameworkanforderungsobjekt sein.

Diese Funktionen werden nur von den statischen Analysetools verwendet. Die Funktionen werden vom Compiler ignoriert.

Das folgende Codebeispiel zeigt, wie die Funktionen __sdv_save_request und __sdv_retrieve_request verwendet werden, um SDV zu leiten, sodass SDV die verzögerte Anforderung zuordnen kann. SDV kann diese Zuordnung verwenden, um die DeferredRequestCompleted-Regel zu überprüfen. Die DeferredRequestCompleted-Regel erfordert, dass __sdv_save_request und __sdv_retrieve_request im Code angezeigt werden. Es gibt zwei Treibereigenschaftenregeln (AliasWithinDispatch, AliasWithinTimerDpc), die nach dem Vorhandensein der Funktionen __sdv_save_request und __sdv_retrieve_request suchen.

Im folgenden Codebeispiel ist die Funktion EchoEvtIoRead eine EvtIoRead-Ereignisrückruffunktion , die das Handle im Frameworkanforderungsobjekt im Kontextbereich der Warteschlange speichert. Die Funktion EchoEvtTimerFunc ist eine EvtTimerFunc-Ereignisrückruffunktion , die sie abruft.

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;
}

Im folgenden Codebeispiel wird veranschaulicht, wie die __sdv_retrieve_request-Funktion eine vorhandene Anforderung ordnet, sodass SDV sie zum Abschluss nachverfolgen kann.

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;
}