Unterstützung für Interruptereignisse hinzufügen

Gehen Sie wie folgt vor, um Ihren WIA-Treiber ordnungsgemäß für die Meldung von Interruptereignissen einzurichten:

  1. Legen Sie Capabilities=0x31 in der INF-Datei des Geräts fest. (Weitere Informationen finden Sie unter INF-Dateien für WIA-Geräte .)

  2. Melden Sie STI_GENCAP_NOTIFICATIONS und STI_USD_GENCAP_NATIVE_PUSHSUPPORT in der IStiUSD::GetCapabilities-Methode .

  3. Melden Sie alle unterstützten Ereignisse in der IWiaMiniDrv::d rvGetCapabilities-Methode .

  4. Zwischenspeichern und Verwenden des Ereignishandles, das in der IStiUSD::SetNotificationHandle-Methode übergeben wird. Dies ist das Ereignishandle, das das Gerät oder der WIA-Minitreiber direkt mithilfe von SetEvent signalisiert (in der Microsoft Windows SDK-Dokumentation beschrieben). Mit dieser Methode initiieren Sie den Wartezustand des WIA-Geräts.

  5. Melden Sie die richtige Ereignisinformationsantwort in der IStiUSD::GetNotificationData-Methode .

Die folgenden beiden Beispiele zeigen die Konfiguration Ihres Geräts für Interrupts mit Implementierungen der Methoden IWiaMiniDrv::d rvGetCapabilities und IStiUSD::SetNotificationHandle .

Hinweis Es ist wichtig, überlappende E/A-Aufrufe mit allen Aktivitäten zu verwenden, die die Kernelmodustreiber einbeziehen. Dies ermöglicht ordnungsgemäße Timeouts und das Abbrechen von Geräteanforderungen.

Erläuterung der IWiaMiniDrv::d rvGetCapabilities-Implementierung

Der WIA-Dienst ruft die IWiaMiniDrv::d rvGetCapabilities-Methode auf, um die vom WIA-Gerät unterstützten Ereignisse und Befehle abzurufen. Der WIA-Treiber sollte sich zuerst den eingehenden lFlags-Parameter ansehen, um zu bestimmen, welche Anforderung beantwortet werden soll.

Der WIA-Treiber sollte Arbeitsspeicher zuordnen (der vom WIA-Treiber verwendet und von diesem freigegeben wird), um ein Array von WIA_DEV_CAP_DRV-Strukturen zu enthalten. Übergeben Sie im Aufruf von IWiaMiniDrv::d rvGetCapabilities einen Zeiger auf den Speicherort des Arbeitsspeichers, der die Adresse des vom WIA-Treiber zugewiesenen Arbeitsspeichers im ppCapabilities-Parameter enthält.

Hinweis Der WIA-Dienst gibt diesen Arbeitsspeicher nicht frei. Es ist wichtig, dass der WIA-Treiber den zugeordneten Arbeitsspeicher verwaltet.

Das folgende Beispiel zeigt eine Implementierung der IWiaMiniDrv::d rvGetCapabilities-Methode .

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

Die IStiUSD::SetNotificationHandle-Methode wird vom WIA-Dienst oder intern von diesem Treiber aufgerufen, um Ereignisbenachrichtigungen zu starten oder zu beenden. Der WIA-Dienst übergibt ein gültiges Handle, das mithilfe von CreateEvent (in der Microsoft Windows SDK-Dokumentation beschrieben) erstellt wurde und angibt, dass der WIA-Treiber dieses Handle signalisieren soll, wenn ein Ereignis in der Hardware auftritt.

NULL kann an die IStiUSD::SetNotificationHandle-Methode übergeben werden. NULL gibt an, dass der WIA-Minitreiber alle Geräteaktivitäten beenden und alle Ereigniswartevorgänge beenden soll.

Das folgende Beispiel zeigt eine Implementierung der IStiUSD::SetNotificationHandle-Methode .

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

Wenn der WIA-Minitreiber oder ein WIA-Gerät ein Ereignis erkannt und signalisiert hat, ruft der WIA-Dienst die IStiUSD::GetNotificationData-Methode auf. Bei dieser Methode sollte der WIA-Minitreiber die Details des aufgetretenen Ereignisses melden.

Der WIA-Dienst ruft die IStiUSD::GetNotificationData-Methode auf, um Informationen zu einem Ereignis abzurufen, das gerade signalisiert wurde. Die IStiUSD::GetNotificationData-Methode kann als Ergebnis einer von zwei Ereignisvorgängen aufgerufen werden.

  1. IStiUSD::GetStatus meldete, dass ein Ereignis aussteht, indem das flag STI_EVENTHANDLING_PENDING in der STI_DEVICE_STATUS-Struktur festgelegt wurde.

  2. Das hEvent-Handle, das von IStiUSD::SetNotificationHandle übergeben wurde, wurde von der Hardware oder durch aufrufen von SetEvent (in der Microsoft Windows SDK-Dokumentation beschrieben) signalisiert.

Der WIA-Treiber ist für das Ausfüllen der STINOTIFY-Struktur verantwortlich.

Das folgende Beispiel zeigt eine Implementierung der IStiUSD::GetNotificationData-Methode .

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

Interruptereignisse können jederzeit beendet werden, indem NULL als Ereignishandle übergeben wird. Der Minitreiber sollte dies als Signal interpretieren, um alle Wartezustände auf dem Hardwaregerät zu beenden.

Die IWiaMiniDrv::d rvNotifyPnpEvent-Methode kann Energieverwaltungsereignisse empfangen, die sich auf den Wartezustand des Ereignisses auswirken.

Der WIA-Dienst ruft die IWiaMiniDrv::d rvNotifyPnpEvent-Methode auf und sendet ein WIA_EVENT_POWER_SUSPEND-Ereignis, wenn das System in einen Ruhezustand versetzt werden soll. Wenn dieser Aufruf erfolgt, ist das Gerät möglicherweise bereits nicht mehr im Wartezustand. Im Standbyzustand werden Kernelmodustreiber automatisch ausgelöst, um jeden wartenden Zustand zu beenden, damit das System in diesen heruntergefahrenen Zustand wechseln kann. Wenn das System aus dem Ruhezustand fortgesetzt wird, sendet der WIA-Dienst das WIA_EVENT_POWER_RESUME-Ereignis. Zu diesem Zeitpunkt muss der WIA-Minitreiber den Wartezustand des Interruptereignisses wiederherstellen. Weitere Informationen zu Ruhezustand finden Sie unter System-Energiezustände und Gerätestromzustände.

Es wird empfohlen, dass der WIA-Minitreiber das Ereignishandle zwischenspeichert, das ursprünglich an die IStiUSD::SetNotificationHandle-Methode übergeben wurde, damit es wiederverwendet werden kann, wenn das System aus einem Ruhezustand oder Ruhezustand aktiviert wird.

Der WIA-Dienst ruft die IStiUSD::SetNotificationHandle-Methodenicht auf, nachdem das System fortgesetzt wurde. Es wird empfohlen, dass der Minidriver seine IStiUSD::SetNotificationHandle-Methode aufruft und das zwischengespeicherte Ereignishandle übergibt.

Der WIA-Dienst ruft die IWiaMiniDrv::d rvNotifyPnpEvent-Methode auf, wenn Systemereignisse auftreten. Der WIA-Treiber sollte den pEventGUID-Parameter überprüfen, um zu bestimmen, welches Ereignis verarbeitet wird.

Einige häufige Ereignisse, die verarbeitet werden müssen, sind:

WIA_EVENT_POWER_SUSPEND
Das System wechselt in den Ruhe-/Standbymodus.

WIA_EVENT_POWER_RESUME
Das System wird aus dem Standby-/Standbymodus aktiviert.

Der WIA-Treiber sollte alle Ereignisunterbrechungsstatus wiederherstellen, nachdem er von einem Angehalten zurückgerufen wurde. Dadurch wird sichergestellt, dass die Ereignisse weiterhin funktionieren, wenn das System aktiviert wird.

Das folgende Beispiel zeigt eine Implementierung der IWiaMiniDrv::d rvNotifyPnpEvent-Methode .

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