Биометрический сервис Windows запускает SensorAdapterStartCapture в цикле при вызове WinBioCaptureSample - PullRequest
2 голосов
/ 05 апреля 2019

Я использую биометрический драйвер для Windows с использованием образца umdf из github . Когда я вызываю WinBioCaptureSample , методы следующего плагина запускаются в цикле.

SensorAdapterClearContext
EngineAdapterClearContext
SensorAdapterStartCapture
SensorAdapterFinishCapture

Я использовал TraceView для отладки моего драйвера, и он показывает следующие сообщения трассировки , когда застревает в цикле.

00000001    driver  352840  439560  1   1   04\05\2018-16:46:13:12  CBiometricDevice::OnGetSensorStatus Called.
00000002    driver  352840  439560  1   2   04\05\2018-16:46:13:12  CBiometricDevice::OnGetAttributes Called.
00000003    driver  352840  439560  1   3   04\05\2018-16:46:13:12  CBiometricDevice::OnCaptureDataBuffer too small - must be at least 0x18.
00000004    driver  352840  439560  1   4   04\05\2018-16:46:13:12  CBiometricDevice::OnCaptureData Called.
00000005    driver  352840  439560  4   5   04\05\2018-16:46:13:28  CBiometricDevice::OnGetSensorStatus Called.
00000006    driver  352840  439560  1   6   04\05\2018-16:46:13:29  CBiometricDevice::OnCaptureDataBuffer too small - must be at least 0x18.
00000007    driver  352840  439560  1   7   04\05\2018-16:46:13:29  CBiometricDevice::OnCaptureData Called.
00000008    driver  352840  439560  1   8   04\05\2018-16:46:13:30  CBiometricDevice::OnGetSensorStatus Called.
00000009    driver  352840  439560  4   9   04\05\2018-16:46:13:30  CBiometricDevice::OnCaptureDataBuffer too small - must be at least 0x18.
00000010    driver  352840  439560  1   10  04\05\2018-16:46:13:31  CBiometricDevice::OnCaptureData Called.
...

Метод CBiometricDevice :: OnGetSensorStatus всегда возвращает WINBIO_SENSOR_READY

diagnostics->WinBioHresult = S_OK;    
diagnostics->SensorStatus = WINBIO_SENSOR_READY;    
MyRequest.SetInformation(diagnostics->PayloadSize);
MyRequest.SetCompletionHr(S_OK);

Далее следует метод CBiometricDevice :: OnCaptureData

DWORD WINAPI
CaptureSleepThread(
    LPVOID lpParam
    ) 
{ 
    CBiometricDevice *device = (CBiometricDevice *) lpParam;
    PCAPTURE_SLEEP_PARAMS sleepParams = device->GetCaptureSleepParams();
   if (sleepParams->SleepValue > 60)
    {
        sleepParams->SleepValue = 60;
    }

    Sleep(sleepParams->SleepValue * 1000);

    UCHAR szBuffer[] = { 0x08, 0x01, 0x00, 0x02 };
    ULONG cbRead = 4;

    sleepParams->captureData->WinBioHresult = S_OK;
    sleepParams->captureData->SensorStatus = WINBIO_SENSOR_ACCEPT;
    sleepParams->captureData->RejectDetail = 0;
    sleepParams->captureData->CaptureData.Size = cbRead;

    RtlCopyMemory(sleepParams->captureData->CaptureData.Data, szBuffer, cbRead);

    device->CompletePendingRequest(sleepParams->Hr, sleepParams->Information);

    return 0;
}

CBiometricDevice::OnCaptureData(
    _Inout_ IWDFIoRequest *FxRequest
    )

{
    ULONG controlCode = 0;
    PWINBIO_CAPTURE_PARAMETERS captureParams = NULL;
    SIZE_T inputBufferSize = 0;
    PWINBIO_CAPTURE_DATA captureData = NULL;
    SIZE_T outputBufferSize = 0;
    bool requestPending = false;

    EnterCriticalSection(&m_RequestLock);

    if (m_PendingRequest == NULL) 
    {

        if (m_SleepThread != INVALID_HANDLE_VALUE)
        {
            LeaveCriticalSection(&m_RequestLock);

            // TODO: Add code to signal thread to exit.

            WaitForSingleObject(m_SleepThread, INFINITE);
            CloseHandle(m_SleepThread);
            m_SleepThread = INVALID_HANDLE_VALUE;

            EnterCriticalSection(&m_RequestLock);
        }

        if (m_PendingRequest == NULL)
        {
            m_PendingRequest = FxRequest;
            m_PendingRequest->MarkCancelable(this);
        }
        else
        {
            requestPending = true;
        }

    } 
    else 
    {
        requestPending = true;
    }

    LeaveCriticalSection(&m_RequestLock);

    if (requestPending)
    {
        FxRequest->Complete(WINBIO_E_DATA_COLLECTION_IN_PROGRESS);
        return;
    }

    GetIoRequestParams(FxRequest,
                       &controlCode,
                       (PUCHAR *)&captureParams,
                       &inputBufferSize,
                       (PUCHAR *)&captureData,
                       &outputBufferSize);

     if (inputBufferSize < sizeof (WINBIO_CAPTURE_PARAMETERS)) 
     {
        TraceEvents(TRACE_LEVEL_ERROR, 
                   BIOMETRIC_TRACE_DEVICE, 
                   "%!FUNC!Invalid argument(s).");
        CompletePendingRequest(E_INVALIDARG, 0);
        return;
    }

    if (outputBufferSize < sizeof(DWORD)) 
    {
        TraceEvents(TRACE_LEVEL_ERROR, 
                   BIOMETRIC_TRACE_DEVICE, 
                   "%!FUNC!Output buffer NULL or too small to return size information.");
        CompletePendingRequest(E_INVALIDARG, 0);
        return;
    }

    if (outputBufferSize < sizeof (WINBIO_CAPTURE_DATA)) 
    {
        TraceEvents(TRACE_LEVEL_ERROR, 
                   BIOMETRIC_TRACE_DEVICE, 
                   "%!FUNC!Buffer too small - must be at least 0x%x.", sizeof (WINBIO_CAPTURE_DATA));

        DWORD cbSize = 262144;//obtained from MAXIMUM_TRANSFER_SIZE policy of WinUsb
        captureData->PayloadSize = (DWORD) sizeof(WINBIO_CAPTURE_DATA) + cbSize;
        CompletePendingRequest(S_OK, sizeof(DWORD));
        return;
    }

    RtlZeroMemory(captureData, outputBufferSize);

    captureData->PayloadSize = (DWORD) outputBufferSize;// (DWORD) sizeof(WINBIO_CAPTURE_DATA);
    captureData->WinBioHresult = WINBIO_E_NO_CAPTURE_DATA;
    captureData->SensorStatus = WINBIO_SENSOR_FAILURE;
    captureData->RejectDetail= 0;
    captureData->CaptureData.Size = 0;

    if (captureParams->Purpose == WINBIO_NO_PURPOSE_AVAILABLE)
    {
        captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_PURPOSE;
    }
    else if ((captureParams->Format.Type != WINBIO_ANSI_381_FORMAT_TYPE) ||
             (captureParams->Format.Owner != WINBIO_ANSI_381_FORMAT_OWNER))
    {
        captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_DATA_FORMAT;
    }
    else if (captureParams->Flags != WINBIO_DATA_FLAG_RAW)
    {
        captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_DATA_TYPE;
    }

    struct _WINUSB_PIPE_INFORMATION InputPipeInfo, OutputPipeInfo;
    m_pIUsbInputPipe->GetInformation(&InputPipeInfo);
    m_pIUsbOutputPipe->GetInformation(&OutputPipeInfo);

    m_SleepParams.PipeInId = InputPipeInfo.PipeId;
    m_SleepParams.PipeOutId = OutputPipeInfo.PipeId;
    m_SleepParams.hDeviceHandle = m_pIUsbInterface->GetWinUsbHandle();
    m_SleepParams.cbSize = (DWORD) outputBufferSize;

    m_SleepParams.SleepValue = 5;
    m_SleepParams.Hr = S_OK;
    m_SleepParams.Information = captureData->PayloadSize;
    m_SleepParams.captureData = captureData;
    m_SleepThread = CreateThread(NULL,                   // default security attributes
                                 0,                      // use default stack size  
                                 CaptureSleepThread,     // thread function name
                                 this,                   // argument to thread function 
                                 0,                      // use default creation flags 
                                 NULL);                  // returns the thread identifier 

    TraceEvents(TRACE_LEVEL_ERROR,
        BIOMETRIC_TRACE_DEVICE,
        "%!FUNC! Called.");
}

Методы SensorAdapterStartCapture и SensorAdapterFinishCapture возвращает S_OK

static HRESULT
WINAPI
SensorAdapterStartCapture(
    _Inout_ PWINBIO_PIPELINE Pipeline,
    _In_ WINBIO_BIR_PURPOSE Purpose,
    _Out_ LPOVERLAPPED *Overlapped
    )
{
    HRESULT hr = S_OK;
    WINBIO_SENSOR_STATUS sensorStatus = WINBIO_SENSOR_FAILURE;
    WINBIO_CAPTURE_PARAMETERS captureParameters = { 0 };
    BOOL result = TRUE;
    DWORD bytesReturned = 0;

    // Verify that pointer arguments are not NULL.
    if (!ARGUMENT_PRESENT(Pipeline) ||
        !ARGUMENT_PRESENT(Purpose) ||
        !ARGUMENT_PRESENT(Overlapped))
    {
        return E_POINTER;

    }

    // Retrieve the context from the pipeline.
    PWINIBIO_SENSOR_CONTEXT sensorContext =
        (PWINIBIO_SENSOR_CONTEXT)Pipeline->SensorContext;

    // Verify the state of the pipeline.
    if (sensorContext == NULL ||
        Pipeline->SensorHandle == INVALID_HANDLE_VALUE)
    {
        return WINBIO_E_INVALID_DEVICE_STATE;
    }

    *Overlapped = NULL;

    //  Synchronously retrieve the status.
    hr = SensorAdapterQueryStatus(Pipeline, &sensorStatus);
    if (FAILED(hr))
    {
        return hr;
    }

    // Determine whether the sensor requires calibration.
    //if (sensorStatus == WINBIO_SENSOR_NOT_CALIBRATED)
    //{
        // Call a custom function that sends IOCTLs to
        // the sensor to calibrate it. This operation is
        // synchronous.
        //hr = _SensorAdapterCalibrate(Pipeline);

        // Retrieve the status again to determine whether the 
        // sensor is ready.
        //if (SUCCEEDED(hr))
        //{
        //    hr = SensorAdapterQueryStatus(Pipeline, &sensorStatus);
        //}

        //if (FAILED(hr))
        //{
        //    return hr;
        //}
    //}
    if (sensorStatus == WINBIO_SENSOR_BUSY)
    {
        return WINBIO_E_DEVICE_BUSY;
    }

    if (sensorStatus != WINBIO_SENSOR_READY)
    {
        return WINBIO_E_INVALID_DEVICE_STATE;
    }

    // Determine whether the data format has been previously determined.
    // If it has not, find a format supported by both the engine and 
    // the sensor.
    if ((sensorContext->Format.Owner == 0) &&
        (sensorContext->Format.Type == 0))
    {

        // Retrieve the format preferred by the engine.
        hr = Pipeline->EngineInterface->QueryPreferredFormat(
            Pipeline,
            &sensorContext->Format,
            &sensorContext->VendorFormat
        );
        if (SUCCEEDED(hr))
        {
            // Call a private function that queries the sensor driver
            // and attaches an attribute array to the sensor context.
            // This operation is synchronous.
            hr = _SensorAdapterGetAttributes(Pipeline);
        }

        if (SUCCEEDED(hr))
        {
            // Search the sensor attributes array for the format
            // preferred by the engine adapter.
            DWORD i = 0;
            for (i = 0; i < sensorContext->AttributesBuffer->SupportedFormatEntries; i++)
            {
                if ((sensorContext->AttributesBuffer->SupportedFormat[i].Owner == sensorContext->Format.Owner) &&
                    (sensorContext->AttributesBuffer->SupportedFormat[i].Type == sensorContext->Format.Type))
                {
                    break;
                }
            }

            if (i == sensorContext->AttributesBuffer->SupportedFormatEntries)
            {
                // No match was found. Use the default.
                sensorContext->Format.Owner = WINBIO_ANSI_381_FORMAT_OWNER;
                sensorContext->Format.Type = WINBIO_ANSI_381_FORMAT_TYPE;
            }
        }
        else
        {
            return hr;
        }
    }

    // Set up the parameter-input block needed for the IOCTL.
    captureParameters.PayloadSize = sizeof(WINBIO_CAPTURE_PARAMETERS);
    captureParameters.Purpose = Purpose;
    captureParameters.Format.Owner = sensorContext->Format.Owner;
    captureParameters.Format.Type = sensorContext->Format.Type;
    CopyMemory(&captureParameters.VendorFormat, &sensorContext->VendorFormat, sizeof(WINBIO_UUID));
    captureParameters.Flags = WINBIO_DATA_FLAG_RAW;

     // Determine whether a buffer has already been allocated for this sensor.
    if (sensorContext->CaptureBuffer == NULL)
    {
        DWORD allocationSize = 0;

        sensorContext->CaptureBufferSize = 0;

        // This sample assumes that the sensor driver returns
        // a fixed-size DWORD buffer containing the required
        // size of the capture buffer if it receives a buffer
        // that is smaller than sizeof(WINBIO_CAPTURE_DATA).
        //
        // Call the driver with a small buffer to get the 
        // allocation size required for this sensor.
        //
        // Because this operation is asynchronous, you must block 
        // and wait for it to complete.
        result = DeviceIoControl(
            Pipeline->SensorHandle,
            IOCTL_BIOMETRIC_CAPTURE_DATA,
            &captureParameters,
            sizeof(WINBIO_CAPTURE_PARAMETERS),
            &allocationSize,
            sizeof(DWORD),
            &bytesReturned,
           &sensorContext->Overlapped
        );
        if (!result && GetLastError() == ERROR_IO_PENDING)
        {
            SetLastError(ERROR_SUCCESS);

            result = GetOverlappedResult(
                Pipeline->SensorHandle,
                &sensorContext->Overlapped,
                &bytesReturned,
                TRUE
            );
        }

        if (!result || bytesReturned != sizeof(DWORD))
        {
            // An error occurred.
            hr = _AdapterGetHresultFromWin32(GetLastError());
            return hr;
        }

        // Make sure that you allocate at least the minimum buffer 
        // size needed to get the payload structure.
        if (allocationSize < sizeof(WINBIO_CAPTURE_DATA))
        {
            allocationSize = sizeof(WINBIO_CAPTURE_DATA);
        }

        // Allocate the buffer.
        sensorContext->CaptureBuffer = (PWINBIO_CAPTURE_DATA)_AdapterAlloc(allocationSize);
        if (!sensorContext->CaptureBuffer)
        {
            sensorContext->CaptureBufferSize = 0;
            return E_OUTOFMEMORY;
        }
        sensorContext->CaptureBufferSize = allocationSize;
    }
    else
    {
        // The buffer has already been allocated. Clear the buffer contents. 
        SensorAdapterClearContext(Pipeline);
    }

    // Send the capture request. Because this is an asynchronous operation,
    // the IOCTL call will return immediately regardless of 
    // whether the I/O has completed.
    result = DeviceIoControl(
        Pipeline->SensorHandle,
        IOCTL_BIOMETRIC_CAPTURE_DATA,
        &captureParameters,
        sizeof(WINBIO_CAPTURE_PARAMETERS),
        sensorContext->CaptureBuffer,
        (DWORD)sensorContext->CaptureBufferSize,
        &bytesReturned,
        &sensorContext->Overlapped
    );

    if (result ||
        (!result && GetLastError() == ERROR_IO_PENDING))
    {
        *Overlapped = &sensorContext->Overlapped;
        return S_OK;
    }
    else
    {
        hr = _AdapterGetHresultFromWin32(GetLastError());
        return hr;
    }

}

static HRESULT
WINAPI
SensorAdapterFinishCapture(
    _Inout_ PWINBIO_PIPELINE Pipeline,
    _Out_ PWINBIO_REJECT_DETAIL RejectDetail
    )
{
    HRESULT hr = S_OK;
    //WINBIO_SENSOR_STATUS sensorStatus = WINBIO_SENSOR_FAILURE;
    WINBIO_CAPTURE_PARAMETERS captureParameters = { 0 };
    BOOL result = TRUE;
    DWORD bytesReturned = 0;

    // Verify that pointer arguments are not NULL.
    if (!ARGUMENT_PRESENT(Pipeline) ||
        !ARGUMENT_PRESENT(RejectDetail))
    {
        return E_POINTER;        
    }

    // Retrieve the context from the pipeline.
    PWINIBIO_SENSOR_CONTEXT sensorContext =
        (PWINIBIO_SENSOR_CONTEXT)Pipeline->SensorContext;

    // Verify the state of the pipeline.
    if (sensorContext == NULL ||
        Pipeline->SensorHandle == INVALID_HANDLE_VALUE)
    {
        return WINBIO_E_INVALID_DEVICE_STATE;
    }

    // Initialize the RejectDetail argument.
    *RejectDetail = 0;

    // Wait for I/O completion. This sample assumes that the I/O operation was 
    // started using the code example shown in the SensorAdapterStartCapture
    // documentation.
    SetLastError(ERROR_SUCCESS);

    result = GetOverlappedResult(
        Pipeline->SensorHandle,
        &sensorContext->Overlapped,
        &bytesReturned,
        TRUE
    );
    if (!result)
    {
        // There was an I/O error.
        return _AdapterGetHresultFromWin32(GetLastError());
    }

    if (bytesReturned == sizeof(DWORD))
    {
        // The buffer is not large enough.  This can happen if a device needs a 
        // bigger buffer depending on the purpose. Allocate a larger buffer and 
        // force the caller to reissue their I/O request.
        DWORD allocationSize = sensorContext->CaptureBuffer->PayloadSize;

        // Allocate at least the minimum buffer size needed to retrieve the 
        // payload structure.
        if (allocationSize < sizeof(WINBIO_CAPTURE_DATA))
        {
            allocationSize = sizeof(WINBIO_CAPTURE_DATA);
        }

        // Free the old buffer and allocate a new one.
        _AdapterRelease(sensorContext->CaptureBuffer);
        sensorContext->CaptureBuffer = NULL;

        sensorContext->CaptureBuffer =
            (PWINBIO_CAPTURE_DATA)_AdapterAlloc(allocationSize);
        if (sensorContext->CaptureBuffer == NULL)
        {
            sensorContext->CaptureBufferSize = 0;
            return E_OUTOFMEMORY;
        }
        sensorContext->CaptureBufferSize = allocationSize;
        return WINBIO_E_BAD_CAPTURE;
    }

    // Normalize the status value before sending it back to the biometric service.
    if (sensorContext->CaptureBuffer != NULL &&
        sensorContext->CaptureBufferSize >= sizeof(WINBIO_CAPTURE_DATA))
    {
        switch (sensorContext->CaptureBuffer->SensorStatus)
        {
        case WINBIO_SENSOR_ACCEPT:
        {
            // The capture was acceptable.
            DWORD cbRead = sensorContext->CaptureBuffer->CaptureData.Size;
            UCHAR * data = sensorContext->CaptureBuffer->CaptureData.Data;
            //wprintf(L"%d ", cbRead);
            break;
        }
        case WINBIO_SENSOR_REJECT:
            // The capture was not acceptable. Overwrite the WinBioHresult value
            // in case it has not been properly set.
            sensorContext->CaptureBuffer->WinBioHresult = WINBIO_E_BAD_CAPTURE;
            break;

        case WINBIO_SENSOR_BUSY:
            // The device is busy. Reset the WinBioHresult value in case it 
            // has not been properly set.
            sensorContext->CaptureBuffer->WinBioHresult = WINBIO_E_DEVICE_BUSY;
            break;

        case WINBIO_SENSOR_READY:
        case WINBIO_SENSOR_NOT_CALIBRATED:
        case WINBIO_SENSOR_FAILURE:
        default:
            // There has been a device failure. Reset the WinBioHresult value
            // in case it has not been properly set.
            sensorContext->CaptureBuffer->WinBioHresult = WINBIO_E_INVALID_DEVICE_STATE;
            break;
        }

        *RejectDetail = sensorContext->CaptureBuffer->RejectDetail;
        hr = sensorContext->CaptureBuffer->WinBioHresult;
    }
    else
    {
        // The buffer is not large enough or the buffer pointer is NULL.
        hr = WINBIO_E_INVALID_DEVICE_STATE;
    }
    return hr;

}

Я использовал следующий код из этого проекта github

HRESULT CaptureSample()
{
  HRESULT hr = S_OK;
  WINBIO_SESSION_HANDLE sessionHandle = NULL;
  WINBIO_UNIT_ID unitId = 0;
  WINBIO_REJECT_DETAIL rejectDetail = 0;
  PWINBIO_BIR sample = NULL;
  SIZE_T sampleSize = 0;

  // Connect to the system pool. 
  hr = WinBioOpenSession(
    WINBIO_TYPE_FINGERPRINT,    // Service provider
    WINBIO_POOL_SYSTEM,         // Pool type
    WINBIO_FLAG_RAW,            // Access: Capture raw data //To call WinBioCaptureSample function successfully, you must open the session handle by specifying WINBIO_FLAG_RAW
                                //WINBIO_FLAG_RAW: The client application captures raw biometric data using WinBioCaptureSample.
    NULL,                       // Array of biometric unit IDs //NULL if the PoolType parameter is WINBIO_POOL_SYSTEM
    0,                          // Count of biometric unit IDs//zero if the PoolType parameter is WINBIO_POOL_SYSTEM.
    WINBIO_DB_DEFAULT,          // Default database
    &sessionHandle              // [out] Session handle
    );

  if(FAILED(hr))
  {
    std::cout << "WinBioOpenSession failed. hr = 0x" << std::hex << hr << std::dec << "\n";

    if(sample != NULL)
    {
      WinBioFree(sample);
      sample = NULL;
    }

    if(sessionHandle != NULL)
    {
      WinBioCloseSession(sessionHandle);
      sessionHandle = NULL;
    }

    return hr;
  }

  // Capture a biometric sample.
  std::cout << "Calling WinBioCaptureSample - Swipe sensor...\n";

  hr = WinBioCaptureSample(
    sessionHandle,
    WINBIO_NO_PURPOSE_AVAILABLE,
    WINBIO_DATA_FLAG_RAW,//WINBIO_DATA_FLAG_RAW
    &unitId,
    &sample,
    &sampleSize,
    &rejectDetail
    );

  if(FAILED(hr))
  {
    if(hr == WINBIO_E_BAD_CAPTURE)
      std:: cout << "Bad capture; reason: " << rejectDetail << "\n";
    else
      std::cout << "WinBioCaptureSample failed.hr = 0x" << std::hex << hr << std::dec << "\n";

    if(sample != NULL)
    {
      WinBioFree(sample);
      sample = NULL;
    }

    if(sessionHandle != NULL)
    {
      WinBioCloseSession(sessionHandle);
      sessionHandle = NULL;
    }

    return hr;
  }

  std::cout << "Swipe processed - Unit ID: " << unitId << "\n";
  std::cout << "Captured " << sampleSize << " bytes.\n";

  if(sample != NULL)
  {            
    WinBioFree(sample);
    sample = NULL;
  }

  if(sessionHandle != NULL)
  {
    WinBioCloseSession(sessionHandle);
    sessionHandle = NULL;
  }

  return hr;
}


int main()
{
  EnumerateSensors();
  CreateDirectoryA("data", NULL);
  CaptureSample();
}

Иногда мой код застревает в цикле, а иногда нет: (

Любой намек приветствуется. Спасибо.

1 Ответ

1 голос
/ 09 апреля 2019

Кажется, я мог бы найти временное решение своей проблемы.Сначала я изменил режим датчика с базового на расширенный.

[DriverPlugInAddReg]
HKR,WinBio\Configurations,DefaultConfiguration,,"0"
HKR,WinBio\Configurations\0,SensorMode,0x10001,2                                ; Basic - 1, Advanced - 2

И цикл не запустится

Но я также заметил, что если я прокомментирую вызов Sleep метод внутри CaptureSleepThread цикл начинается снова.

Я предполагаю, что Windows Biometric Service ожидает метод CaptureSleepThread длячтобы считаться успешным, требуется некоторое время, но если метод завершается очень быстро, он считается неуспешным, несмотря на успешные ответы S_OK и WINBIO_SENSOR_ACCEPT , и Windows Biometric Service будет повторять попытку, вызывая SensorAdapterStartCapture , вызывающий цикл.

DWORD WINAPI
CaptureSleepThread(
    LPVOID lpParam
    ) 
{ 
    CBiometricDevice *device = (CBiometricDevice *) lpParam;
    PCAPTURE_SLEEP_PARAMS sleepParams = device->GetCaptureSleepParams();

    //
    // 1 minute or half a minute delay is the trick.
    //
   if (sleepParams->SleepValue > 60)
    {
        sleepParams->SleepValue = 60;
    }

    Sleep(sleepParams->SleepValue * 1000);

    UCHAR szBuffer[] = { 0x08, 0x01, 0x00, 0x02 };
    ULONG cbRead = 4;

    sleepParams->captureData->WinBioHresult = S_OK;
    sleepParams->captureData->SensorStatus = WINBIO_SENSOR_ACCEPT;
    sleepParams->captureData->RejectDetail = 0;
    sleepParams->captureData->CaptureData.Size = cbRead;

    RtlCopyMemory(sleepParams->captureData->CaptureData.Data, szBuffer, cbRead);

    device->CompletePendingRequest(sleepParams->Hr, sleepParams->Information);

    return 0;
}

Другие вещи, которые я наблюдал, могут вызвать цикл, когда происходит сбой вызова и установлен неправильный ответ.

//No delay is going to cause a loop
//Sleep(sleepParams->SleepValue * 1000);

//zeroes buffer is considered failed
UCHAR szBuffer[] = { 0x00, 0x00, 0x00, 0x00 };
//zero size is a failed read
ULONG cbRead = 0;
//returning other than S_FALSE or S_OK will cause a loop
sleepParams->captureData->WinBioHresult = S_FALSE;
sleepParams->captureData->SensorStatus = WINBIO_SENSOR_ACCEPT;
sleepParams->captureData->RejectDetail = 0;
sleepParams->captureData->CaptureData.Size = cbRead;
//It is going to fail if CaptureData.Data is empty
//RtlCopyMemory(sleepParams->captureData->CaptureData.Data, szBuffer, cbRead);

Также присоединениетакой отладчик, как WinDbg или Visual Studio, вызовет цикл, поэтому лучше отлаживать, используя только сообщения Trace и инструмент TraceView, и при необходимости присоединять отладчикв этом случае ожидается цикл, просто проигнорируйте его.

...