Partilhar via


Adicionar suporte a eventos de interrupção

Para configurar corretamente o driver WIA para relatar eventos de interrupção, faça o seguinte:

  1. Defina Capabilities=0x31 no arquivo INF do dispositivo. (Consulte Arquivos INF para dispositivos WIA para obter detalhes.)

  2. Informe STI_GENCAP_NOTIFICATIONS e STI_USD_GENCAP_NATIVE_PUSHSUPPORT no método IStiUSD::GetCapabilities.

  3. Relatar todos os eventos suportados no método IWiaMiniDrv::drvGetCapabilities.

  4. Armazene em cache e use o identificador de evento passado no método IStiUSD::SetNotificationHandle . Este é o manipulador de evento que o dispositivo sinaliza, ou o minidriver WIA sinaliza diretamente usando SetEvent (conforme descrito na documentação do SDK do Microsoft Windows). É nesse método que você inicia o estado de espera do dispositivo WIA.

  5. Informe a resposta correta sobre informações de evento no método IStiUSD::GetNotificationData.

Os dois exemplos a seguir mostram a configuração do dispositivo para interrupções com implementações dos métodos IWiaMiniDrv::d rvGetCapabilities e IStiUSD::SetNotificationHandle .

Observação É importante usar chamadas de E/S sobrepostas com todas as atividades que envolvem os drivers do modo kernel. Isso permite tempos de espera adequados e o cancelamento de pedidos de dispositivos.

Explicação da implementação do IWiaMiniDrv::drvGetCapabilities

O serviço WIA chama o método IWiaMiniDrv::d rvGetCapabilities para obter os eventos e comandos suportados pelo dispositivo WIA. O driver WIA deve primeiro examinar o parâmetro lFlags de entrada para determinar qual solicitação ele deve responder.

O driver WIA deve alocar memória (para ser usado pelo driver WIA e liberado por ele) para conter uma matriz de estruturas WIA_DEV_CAP_DRV . Na chamada para o IWiaMiniDrv::drvGetCapabilities, passe um ponteiro para o local de memória que contém o endereço da memória alocada pelo driver WIA no parâmetro ppCapabilities.

Observação O serviço WIA não liberará essa memória. É importante que o driver WIA gerencie a memória alocada.

O exemplo a seguir mostra uma implementação do método IWiaMiniDrv::d rvGetCapabilities .

HRESULT _stdcall CWIADevice::drvGetCapabilities(
  BYTE            *pWiasContext,
  LONG            lFlags,
  LONG            *pcelt,
  WIA_DEV_CAP_DRV **ppCapabilities,
  LONG            *plDevErrVal)
{
  //
  // If the caller did not pass in the correct parameters,
  //  then fail the call and return E_INVALIDARG.
  //

  if (!pWiasContext) {

    //
    // The WIA service may pass in a NULL for the pWiasContext. 
    // This is expected because there is a case where no item 
    // was created at the time the event was fired.
    //
  }

  if (!plDevErrVal) {
      return E_INVALIDARG;
  }

  if (!pcelt) {
      return E_INVALIDARG;
  }

  if (!ppCapabilities) {
      return E_INVALIDARG;
  }

  *plDevErrVal = 0;

  HRESULT hr = S_OK;

  LONG lNumberOfCommands = 1;
  LONG lNumberOfEvents   = 2;

  //
  // initialize WIA driver capabilities ARRAY
  // a member WIA_DEV_CAP_DRV m_Capabilities[3] variable
  // This memory should live with the WIA minidriver.
  // A pointer to this structure is given to the WIA service using
  // ppCapabilities.  Do not delete this memory until
  // the WIA minidriver has been unloaded.
  //

  // This ARRAY should only be initialized once.
  // The Descriptions and Names should be read from the proper
  // string resource.  These string values should be localized in
  // multiple languages because an application will be use them to
  // be displayed to the user.
  //

  // Command #1
  m_Capabilities[0].wszDescription =   L"Synchronize Command";
  m_Capabilities[0].wszName = L"Synchronize";
  m_Capabilities[0].guid    = (GUID*)&WIA_CMD_SYNCHRONIZE;
  m_Capabilities[0].lFlags = 0;
  m_Capabilities[0].wszIcon = WIA_ICON_SYNCHRONIZE;

  // Event #1
  m_Capabilities[1].wszDescription = L"Scan Button";
  m_Capabilities[1].wszName = L"Scan";
  m_Capabilities[1].guid    = (GUID*)&WIA_EVENT_SCAN_IMAGE;
  m_Capabilities[1].lFlags = WIA_NOTIFICATION_EVENT | WIA_ACTION_EVENT;
  m_Capabilities[1].wszIcon = WIA_ICON_SCAN_BUTTON_PRESS;

  // Event #2
  m_Capabilities[2].wszDescription = L"Copy Button";
  m_Capabilities[2].wszName = L"Copy";
  m_Capabilities[2].guid    = (GUID*)&WIA_EVENT_SCAN_PRINT_IMAGE;
  m_Capabilities[2].lFlags = WIA_NOTIFICATION_EVENT | WIA_ACTION_EVENT;
  m_Capabilities[2].wszIcon = WIA_ICON_SCAN_BUTTON_PRESS;


  //
  //  Return depends on flags.  Flags specify whether we should return
  //  commands, events, or both.
  //
  //

  switch (lFlags) {
  case WIA_DEVICE_COMMANDS:

    //
    //  report commands only
    //

    *pcelt          = lNumberOfCommands;
    *ppCapabilities = &m_Capabilities[0];
    break;
  case WIA_DEVICE_EVENTS:

    //
    //  report events only
    //

    *pcelt          = lNumberOfEvents;
    *ppCapabilities = &m_Capabilities[1]; // start at the first event in the ARRAY
    break;
  case (WIA_DEVICE_COMMANDS | WIA_DEVICE_EVENTS):

    //
    //  report both events and commands
    //

     *pcelt          = (lNumberOfCommands + lNumberOfEvents);
     *ppCapabilities = &m_Capabilities[0];
     break;
  default:

    //
    //  invalid request
    //
    hr = E_INVALIDARG;
    break;
  }

  return hr;
}

O método IStiUSD::SetNotificationHandle é chamado pelo serviço WIA ou internamente por este driver para iniciar ou parar notificações de eventos. O serviço WIA passará um handle válido, criado usando CreateEvent (descrito na documentação do SDK do Microsoft Windows), indicando que o driver WIA deve sinalizar esse handle quando ocorra um evento no hardware.

NULL pode ser passado para o método IStiUSD::SetNotificationHandle . NULL indica que o minidriver WIA deve parar toda a atividade do dispositivo e interromper quaisquer operações de espera de eventos.

O exemplo a seguir mostra uma implementação do método IStiUSD::SetNotificationHandle .

STDMETHODIMP CWIADevice::SetNotificationHandle(HANDLE hEvent)
{
  HRESULT hr = S_OK;

  if (hEvent && (hEvent != INVALID_HANDLE_VALUE)) {

    //
    // A valid handle indicates that we are asked to start our "wait"
    // for device interrupt events
    //

    //
    // reset last event GUID to GUID_NULL
    //

    m_guidLastEvent = GUID_NULL;

    //
    // clear EventOverlapped structure
    //

    memset(&m_EventOverlapped,0,sizeof(m_EventOverlapped));

    //
    // fill overlapped hEvent member with the event passed in by 
    // the WIA service. This handle will be automatically signaled
    //  when an event is triggered at the hardware level.
    //

    m_EventOverlapped.hEvent = hEvent;

    //
    // clear event data buffer.  This is the buffer that will be used
    //  to determine what event was signaled from the device.
    //

    memset(m_EventData,0,sizeof(m_EventData));

    //
    // use the following call for interrupt events on your device
    //

    DWORD dwError = 0;
    BOOL bResult = DeviceIoControl( m_hDeviceDataHandle,
                                    IOCTL_WAIT_ON_DEVICE_EVENT,
                                    NULL,
                                    0,
                                    &m_EventData,
                                    sizeof(m_EventData),
                                    &dwError,
                                    &m_EventOverlapped );

    if (bResult) {
        hr = S_OK;
    } else {
        hr = HRESULT_FROM_WIN32(::GetLastError());
    }

  } else {

    //
    // stop any hardware waiting events here, the WIA service has
    // notified us to stop all hardware event waiting
    //

    //
    // Stop hardware interrupt events. This will stop all activity on
    // the device. Since DeviceIOControl was used with OVERLAPPED i/o 
    // functionality the CancelIo() can be used to stop all kernel
    // mode activity.
    //


    if(m_hDeviceDataHandle){
        if(!CancelIo(m_hDeviceDataHandle)){

            //
            // canceling of the IO failed, call GetLastError() here to determine the cause.
            //

            LONG lError = ::GetLastError();

        }
    }
  }
  return hr;
}

Quando o minidriver WIA ou um dispositivo WIA detetou e sinalizou um evento, o serviço WIA chama o método IStiUSD::GetNotificationData . É neste método que o minidriver WIA deve relatar os detalhes do evento que ocorreu.

O serviço WIA chama o método IStiUSD::GetNotificationData para obter informações sobre um evento que acabou de ser sinalizado. O método IStiUSD::GetNotificationData pode ser chamado como resultado de uma das duas operações de evento.

  1. IStiUSD::GetStatus relatou que havia um evento pendente definindo o sinalizador STI_EVENTHANDLING_PENDING na estrutura STI_DEVICE_STATUS .

  2. O identificador hEvent passado em IStiUSD::SetNotificationHandle foi sinalizado pelo hardware ou pela chamada de SetEvent (descrito na documentação do SDK do Microsoft Windows).

O controlador WIA é responsável pelo preenchimento da estrutura STINOTIFY

O exemplo a seguir mostra uma implementação do método IStiUSD::GetNotificationData .

STDMETHODIMP CWIADevice::GetNotificationData( LPSTINOTIFY pBuffer )
{
  //
  // If the caller did not pass in the correct parameters,
  // then fail the call with E_INVALIDARG.
  //

  if(!pBuffer){
      return E_INVALIDARG;
  }
 
  GUID guidEvent = GUID_NULL;
  DWORD dwBytesRet = 0;
  BOOL bResult = GetOverlappedResult(m_hDeviceDataHandle, &m_EventOverlapped, &dwBytesRet, FALSE );
  if (bResult) {
    //
    // read the m_EventData buffer to determine the proper event.
    // set guidEvent to the proper event GUID
    // set guidEvent to GUID_NULL when an event has
    // not happened that you are concerned with
    //

    if(m_EventData[0] == DEVICE_SCAN_BUTTON_PRESSED) {
       guidEvent = WIA_EVENT_SCAN_IMAGE;
    } else {
       guidEvent = GUID_NULL;
    }
  }

  //
  // If the event was triggered, then fill in the STINOTIFY structure
  // with the proper event information
  //

  if (guidEvent != GUID_NULL) {
    memset(pBuffer,0,sizeof(STINOTIFY));
    pBuffer->dwSize               = sizeof(STINOTIFY);
    pBuffer->guidNotificationCode = guidEvent;        
  } else {
    return STIERR_NOEVENTS;
  }

  return S_OK;
}

Os eventos de interrupção podem ser interrompidos a qualquer momento ao passar NULL como identificador do evento. O minidriver deve interpretar isso como um sinal para parar quaisquer estados de espera no dispositivo de hardware.

O método IWiaMiniDrv::d rvNotifyPnpEvent pode receber eventos de gerenciamento de energia que afetam o estado de espera do evento.

O serviço WIA chama o método IWiaMiniDrv::drvNotifyPnpEvent e envia um evento WIA_EVENT_POWER_SUSPEND quando o sistema está prestes a ser colocado num estado de suspensão. Se essa chamada ocorrer, o dispositivo pode já estar fora de seu estado de espera. Os estados de suspensão acionam automaticamente os drivers de modo kernel para sair de qualquer estado de espera para permitir que o sistema entre nesse estado desligado. Quando o sistema retoma de seu estado de suspensão, o serviço WIA envia o evento WIA_EVENT_POWER_RESUME. Neste momento, o minidriver WIA deve restabelecer o estado de espera do evento de interrupção. Para obter mais informações sobre estados de suspensão, consulte Estados de energia do sistema e Estados de energia do dispositivo.

É recomendável que o minidriver WIA armazene em cache o identificador de evento inicialmente passado para o método IStiUSD::SetNotificationHandle para que ele possa ser reutilizado quando o sistema acordar de uma suspensão ou hibernação.

O serviço WIA não chama o método IStiUSD::SetNotificationHandle após a retomada do sistema. É recomendável que o minidriver chame seu método IStiUSD::SetNotificationHandle , passando o identificador de evento armazenado em cache.

O serviço WIA chama o método IWiaMiniDrv::d rvNotifyPnpEvent quando ocorrem eventos do sistema. O driver WIA deve verificar o parâmetro pEventGUID para determinar qual evento está sendo processado.

Alguns eventos comuns que devem ser processados são:

WIA_EVENT_POWER_SUSPEND
O sistema está entrando em modo de suspensão/soneca.

WIA_EVENT_POWER_RESUME
O sistema está a despertar do modo de suspensão/repouso.

O driver WIA deve restaurar qualquer estado de espera de interrupção de evento depois de retornar de uma suspensão. Isto garante que os eventos continuarão a funcionar quando o sistema for ativado.

O exemplo a seguir mostra uma implementação do método IWiaMiniDrv::d rvNotifyPnpEvent .

HRESULT _stdcall CWIADevice::drvNotifyPnpEvent(
  const GUID *pEventGUID,
  BSTR       bstrDeviceID,
  ULONG      ulReserved)
{
  //
  // If the caller did not pass in the correct parameters,
  // then fail the call with E_INVALIDARG.
  //

  if ((!pEventGUID)||(!bstrDeviceID)) {
      return E_INVALIDARG;
  }

  HRESULT hr = S_OK;

  if(*pEventGUID == WIA_EVENT_POWER_SUSPEND) {

    //
    // disable any driver activity to make sure we properly
    // shut down (the driver is not being unloaded, just disabled)
    //

  } else if(*pEventGUID == WIA_EVENT_POWER_RESUME) {

    //
    // reestablish any event notifications to make sure we properly
    // set up any event waiting status using the WIA service supplied
    // event handle
    //

    if(m_EventOverlapped.hEvent) {

      //
      // call ourselves with the cached EVENT handle given to
      // the WIA driver by the WIA service.
      //

        SetNotificationHandle(m_EventOverlapped.hEvent);
    }
  }
  return hr;
}