Compartir a través de


Agregar compatibilidad con eventos de interrupción

Para configurar correctamente el controlador WIA para notificar eventos de interrupción, haga lo siguiente:

  1. Establezca Capabilities=0x31 en el archivo INF del dispositivo. (Consulte Archivos INF para dispositivos WIA para obtener más información).

  2. Informe STI_GENCAP_NOTIFICATIONS y STI_USD_GENCAP_NATIVE_PUSHSUPPORT en el método IStiUSD::GetCapabilities .

  3. Informe de todos los eventos admitidos en el método IWiaMiniDrv::d rvGetCapabilities .

  4. Almacene en caché y use el identificador de eventos pasado en el método IStiUSD::SetNotificationHandle . Este es el identificador de eventos que indica el dispositivo o las señales de minidriver WIA directamente mediante SetEvent (que se describe en la documentación de Microsoft Windows SDK). Se encuentra en este método que inicia el estado de espera del dispositivo WIA.

  5. Informe de la respuesta de información de eventos adecuada en el método IStiUSD::GetNotificationData .

En los dos ejemplos siguientes se muestra cómo configurar el dispositivo para interrumpir las implementaciones de los métodos IWiaMiniDrv::d rvGetCapabilities e IStiUSD::SetNotificationHandle .

Nota Es importante usar llamadas de E/S superpuestas con todas las actividades relacionadas con los controladores de modo kernel. Esto permite tiempos de espera adecuados y cancelación de solicitudes de dispositivo.

Explicación de la implementación de IWiaMiniDrv::d rvGetCapabilities

El servicio WIA llama al método IWiaMiniDrv::d rvGetCapabilities para obtener los comandos y eventos admitidos por el dispositivo WIA. El controlador WIA debe examinar primero el parámetro lFlags entrante para determinar qué solicitud debe responder.

El controlador WIA debe asignar memoria (para que la use el controlador WIA y liberarla) para contener una matriz de estructuras de WIA_DEV_CAP_DRV . En la llamada a IWiaMiniDrv::d rvGetCapabilities, pase un puntero a la ubicación de memoria que contiene la dirección de la memoria asignada por el controlador WIA en el parámetro ppCapabilities .

Nota El servicio WIA no liberará esta memoria. Es importante que el controlador WIA administre la memoria asignada.

En el ejemplo siguiente se muestra una implementación del 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;
}

El servicio WIA llama al método IStiUSD::SetNotificationHandle o internamente para iniciar o detener las notificaciones de eventos. El servicio WIA pasará un identificador válido, creado con CreateEvent (descrito en la documentación de Microsoft Windows SDK), lo que indica que el controlador WIA es indicar este identificador cuando se produce un evento en el hardware.

NULL se puede pasar al método IStiUSD::SetNotificationHandle . NULL indica que el minidriver WIA es detener toda la actividad del dispositivo y salir de las operaciones de espera de eventos.

En el ejemplo siguiente se muestra una implementación del 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;
}

Cuando el minidriver WIA o un dispositivo WIA ha detectado y señalado un evento, el servicio WIA llama al método IStiUSD::GetNotificationData . Se encuentra en este método que el minidriver WIA debe notificar los detalles del evento que se produjo.

El servicio WIA llama al método IStiUSD::GetNotificationData para obtener información sobre un evento que se acaba de señalar. Se puede llamar al método IStiUSD::GetNotificationData como resultado de una de las dos operaciones de evento.

  1. IStiUSD::GetStatus informó de que había un evento pendiente estableciendo la marca STI_EVENTHANDLING_PENDING en la estructura de STI_DEVICE_STATUS .

  2. El identificador hEvent pasado por IStiUSD::SetNotificationHandle fue señalado por el hardware o llamando a SetEvent (descrito en la documentación de Microsoft Windows SDK).

El controlador WIA es responsable de rellenar la estructura STINOTIFY

En el ejemplo siguiente se muestra una implementación del 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;
}

Los eventos de interrupción se pueden detener en cualquier momento pasando NULL como identificador de eventos. El minidriver debe interpretarlo como una señal para detener los estados de espera en el dispositivo de hardware.

El método IWiaMiniDrv::d rvNotifyPnpEvent puede recibir eventos de administración de energía que afectan al estado de espera del evento.

El servicio WIA llama al método IWiaMiniDrv::d rvNotifyPnpEvent y envía un evento WIA_EVENT_POWER_SUSPEND cuando el sistema está a punto de colocarse en estado de suspensión. Si se produce esta llamada, es posible que el dispositivo ya esté fuera de su estado de espera. Los estados de suspensión desencadenan automáticamente controladores en modo kernel para salir de cualquier estado de espera para permitir que el sistema entre este estado apagado. Cuando el sistema se reanuda desde su estado de suspensión, el servicio WIA envía el evento WIA_EVENT_POWER_RESUME. En este momento, el minidriver WIA debe restablecer el estado de espera del evento de interrupción. Para obtener más información sobre los estados de suspensión, consulte Estados de energía del sistema y Estados de energía del dispositivo.

Se recomienda que el minidriver WIA almacene en caché el identificador de eventos pasado inicialmente al método IStiUSD::SetNotificationHandle para que se pueda reutilizar cuando el sistema se reactiva desde una suspensión o hibernación.

El servicio WIA no llama al método IStiUSD::SetNotificationHandle una vez reanudado el sistema. Se recomienda que el minidriver llame a su método IStiUSD::SetNotificationHandle y pase el identificador de eventos almacenados en caché.

El servicio WIA llama al método IWiaMiniDrv::d rvNotifyPnpEvent cuando se producen eventos del sistema. El controlador WIA debe comprobar el parámetro pEventGUID para determinar qué evento se está procesando.

Algunos eventos comunes que se deben procesar son:

WIA_EVENT_POWER_SUSPEND
El sistema entra en modo de suspensión/suspensión.

WIA_EVENT_POWER_RESUME
El sistema se está despertando del modo de suspensión o suspensión.

El controlador WIA debe restaurar los estados de espera de interrupción de eventos después de volver de una suspensión. Esto garantiza que los eventos seguirán funcionando cuando el sistema se despierte.

En el ejemplo siguiente se muestra una implementación del 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;
}