Condividi tramite


Aggiungere il supporto dell'evento di interruzione

Per configurare correttamente il driver WIA per segnalare gli eventi di interruzione, eseguire le operazioni seguenti:

  1. Impostare Capabilities=0x31 nel file INF del dispositivo. Per informazioni dettagliate, vedere File INF per dispositivi WIA .

  2. Segnalare STI_GENCAP_NOTIFICATIONS e STI_USD_GENCAP_NATIVE_PUSHSUPPORT nel metodo IStiUSD::GetCapabilities .

  3. Segnalare tutti gli eventi supportati nel metodo IWiaMiniDrv::d rvGetCapabilities .

  4. Memorizzare nella cache e usare l'handle eventi passato nel metodo IStiUSD::SetNotificationHandle . Questo è l'handle dell'evento che segnala il dispositivo o il minidriver WIA direttamente usando SetEvent (descritto nella documentazione di Microsoft Windows SDK). Si tratta di questo metodo che si avvia lo stato di attesa del dispositivo WIA.

  5. Segnalare la risposta corretta alle informazioni sugli eventi nel metodo IStiUSD::GetNotificationData .

I due esempi seguenti illustrano la configurazione del dispositivo per gli interruzioni con le implementazioni dei metodi IWiaMiniDrv::d rvGetCapabilities e IStiUSD::SetNotificationHandle .

Nota È importante usare chiamate di I/O sovrapposte con tutte le attività che coinvolgono i driver in modalità kernel. Ciò consente il timeout appropriato e l'annullamento delle richieste del dispositivo.

Spiegazione dell'implementazione di IWiaMiniDrv::d rvGetCapabilities

Il servizio WIA chiama il metodo IWiaMiniDrv::d rvGetCapabilities per ottenere gli eventi e i comandi supportati dal dispositivo WIA. Il driver WIA deve prima esaminare il parametro lFlags in ingresso per determinare quale richiesta deve rispondere.

Il driver WIA deve allocare memoria (da usare dal driver WIA e liberato da esso) per contenere una matrice di strutture WIA_DEV_CAP_DRV . Nella chiamata alla classe IWiaMiniDrv::d rvGetCapabilities passare un puntatore alla posizione di memoria che contiene l'indirizzo della memoria allocata dal driver WIA nel parametro ppCapabilities .

Nota Il servizio WIA non libera questa memoria. È importante che il driver WIA gestisce la memoria allocata.

Nell'esempio seguente viene illustrata un'implementazione del metodo 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;
}

Il metodo IStiUSD::SetNotificationHandle viene chiamato dal servizio WIA o internamente da questo driver per avviare o arrestare le notifiche degli eventi. Il servizio WIA passerà un handle valido, creato usando CreateEvent (descritto nella documentazione di Microsoft Windows SDK), che indica che il driver WIA deve segnalare questo handle quando si verifica un evento nell'hardware.

NULL può essere passato al metodo IStiUSD::SetNotificationHandle . NULL indica che il minidriver WIA consiste nell'arrestare tutte le attività del dispositivo e uscire dalle operazioni di attesa degli eventi.

Nell'esempio seguente viene illustrata un'implementazione del metodo 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 il minidriver WIA o un dispositivo WIA ha rilevato e segnalato un evento, il servizio WIA chiama il metodo IStiUSD::GetNotificationData . Si tratta di questo metodo che il minidriver WIA deve segnalare i dettagli dell'evento che si è verificato.

Il servizio WIA chiama il metodo IStiUSD::GetNotificationData per ottenere informazioni su un evento appena segnalato. Il metodo IStiUSD::GetNotificationData può essere chiamato come risultato di una delle due operazioni dell'evento.

  1. IStiUSD::GetStatus ha segnalato che è stato presente un evento in sospeso impostando il flag di STI_EVENTHANDLING_PENDING nella struttura STI_DEVICE_STATUS.

  2. L'handle hEvent passato da IStiUSD::SetNotificationHandle è stato segnalato dall'hardware oppure chiamando SetEvent (descritto nella documentazione di Microsoft Windows SDK).

Il driver WIA è responsabile della compilazione della struttura STINOTIFY

Nell'esempio seguente viene illustrata un'implementazione del metodo 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;
}

Gli eventi di interruzione possono essere arrestati in qualsiasi momento passando NULL come handle eventi. Il minidriver deve interpretare questo come segnale per arrestare gli stati di attesa nel dispositivo hardware.

Il metodo IWiaMiniDrv::d rvNotifyPnpEvent può ricevere eventi di risparmio energia che influiscono sullo stato di attesa dell'evento.

Il servizio WIA chiama il metodo IWiaMiniDrv::d rvNotifyPnpEvent e invia un evento WIA_EVENT_POWER_SUSPEND quando il sistema sta per essere inserito in uno stato di sospensione. Se si verifica questa chiamata, il dispositivo potrebbe essere già fuori dallo stato di attesa. Gli stati di sospensione attivano automaticamente i driver in modalità kernel per uscire da qualsiasi stato di attesa per consentire al sistema di immettere questo stato inattivo. Quando il sistema riprende dallo stato di sospensione, il servizio WIA invia l'evento di WIA_EVENT_POWER_RESUME. A questo punto, il minidriver WIA deve ristabilire lo stato di attesa dell'evento di interruzione. Per altre informazioni sugli stati di sospensione, vedere Stati di alimentazione del sistema e Stati di alimentazione del dispositivo.

È consigliabile che il minidriver WIA memorizza nella cache inizialmente l'handle eventi passato al metodo IStiUSD::SetNotificationHandle in modo che possa essere riutilizzato quando il sistema viene riattivato da una sospensione o un'ibernazione.

Il servizio WIA non chiama il metodo IStiUSD::SetNotificationHandle dopo la ripresa del sistema. È consigliabile che il minidriver chiami il metodo IStiUSD::SetNotificationHandle , passando l'handle eventi memorizzato nella cache.

Il servizio WIA chiama il metodo IWiaMiniDrv::d rvNotifyPnpEvent quando si verificano eventi di sistema. Il driver WIA deve controllare il parametro pEventGUID per determinare quale evento viene elaborato.

Alcuni eventi comuni che devono essere elaborati sono:

WIA_EVENT_POWER_SUSPEND
Il sistema entra in modalità sospensione/sospensione.

WIA_EVENT_POWER_RESUME
Il sistema viene riattivato dalla modalità sospensione/sospensione.

Il driver WIA deve ripristinare gli stati di attesa dell'interruzione degli eventi dopo aver restituito da una sospensione. Ciò garantisce che gli eventi funzionino ancora quando il sistema viene riattivato.

Nell'esempio seguente viene illustrata un'implementazione del metodo 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;
}