Sdílet prostřednictvím


Přidání podpory událostí přerušení

Pokud chcete ovladač WIA správně nastavit tak, aby hlásil události přerušení, postupujte takto:

  1. Nastavte Capabilities=0x31 v souboru INF zařízení. (Podrobnosti najdete v INF Files pro zařízení WIA.)

  2. STI_GENCAP_NOTIFICATIONS a STI_USD_GENCAP_NATIVE_PUSHSUPPORT sestav v metodě IStiUSD::GetCapabilities.

  3. Hlásit všechny podporované události v metodě IWiaMiniDrv::drvGetCapabilities.

  4. Uložte do mezipaměti a použijte obslužnou rutinu události předanou metodě IStiUSD::SetNotificationHandle. Toto je obslužná rutina, kterou zařízení signalizuje, nebo kterou přímo signalizuje minidriver WIA pomocí SetEvent (popsané v dokumentaci k sadě Microsoft Windows SDK). Touto metodou aktivujete čekací stav zařízení WIA.

  5. V metodě IStiUSD::GetNotificationData nahlašte správnou odpověď na informace o událostech.

Následující dva příklady ukazují, jak konfigurovat zařízení pro přerušení s implementací metod IWiaMiniDrv::drvGetCapabilities a IStiUSD::SetNotificationHandle.

Poznámka Je důležité použít překrývající se vstupně-výstupní volání se všemi aktivitami zahrnujícími ovladače režimu jádra. To umožňuje správné vypršení časového limitu a zrušení požadavků na zařízení.

Vysvětlení implementace IWiaMiniDrv::drvGetCapabilities

Služba WIA volá metodu IWiaMiniDrv::drvGetCapabilities, aby získala události a příkazy podporované zařízením WIA. Ovladač WIA by se měl nejprve podívat na příchozí parametr lFlags a určit, na který požadavek má odpovědět.

Ovladač WIA by měl přidělovat paměť (k použití ovladačem WIA a jeho uvolnění) pro obsahování pole struktur WIA_DEV_CAP_DRV. Ve volání IWiaMiniDrv::drvGetCapabilities, předejte ukazatel na umístění paměti, držící adresu paměti přidělené ovladačem WIA v parametru ppCapabilities.

Poznámka Služba WIA tuto paměť nezvolní. Je důležité, aby ovladač WIA spravoval přidělenou paměť.

Následující příklad ukazuje implementaci metody IWiaMiniDrv::drvGetCapabilities.

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

Metoda IStiUSD::SetNotificationHandle je volána službou WIA nebo interně tímto ovladačem ke spuštění nebo zastavení oznámení událostí. Služba WIA předá platný popisovač vytvořený pomocí CreateEvent (popsané v dokumentaci k sadě Microsoft Windows SDK) označující, že ovladač WIA má signalizovat tento popisovač, když dojde k události v hardwaru.

NULL lze předat metodě IStiUSD::SetNotificationHandle. NULL značí, že minidriver WIA zastaví veškerou aktivitu zařízení a ukončí všechny operace čekání na události.

Následující příklad ukazuje implementaci IStiUSD::SetNotificationHandle metoda.

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

Když minidriver WIA nebo zařízení WIA zjistil a signalizoval událost, služba WIA zavolá metodu IStiUSD::GetNotificationData. V této metodě by minidriver WIA měl hlásit podrobnosti o události, ke které došlo.

Služba WIA volá metodu IStiUSD::GetNotificationData, aby získala informace o události, která byla právě signalizována. Metodu IStiUSD::GetNotificationData lze volat v důsledku jedné ze dvou událostí.

  1. IStiUSD::GetStatus oznámil, že došlo k čekající události, nastavením příznaku STI_EVENTHANDLING_PENDING ve struktuře STI_DEVICE_STATUS.

  2. Popisovač hEvent předaný pomocí IStiUSD::SetNotificationHandle byl signalizován hardwarem nebo voláním SetEvent (popisované v dokumentaci k sadě Microsoft Windows SDK).

Ovladač WIA zodpovídá za vyplnění STINOTIFY struktury

Následující příklad ukazuje implementaci IStiUSD::GetNotificationData metoda.

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

Události přerušení je možné kdykoli zastavit předáním null jako popisovače události. Minidriver by to měl interpretovat jako signál, který zastaví všechny stavy čekání na hardwarovém zařízení.

Metoda IWiaMiniDrv::drvNotifyPnpEvent může přijímat události řízení spotřeby, které ovlivňují stav čekání na událost.

Služba WIA volá metodu IWiaMiniDrv::drvNotifyPnpEvent a odešle událost WIA_EVENT_POWER_SUSPEND, když se systém chystá přejít do režimu spánku. Pokud k tomuto volání dojde, zařízení už nemusí být ve stavu čekání. Stavy spánku automaticky aktivují ovladače režimu jádra, aby ukončily jakýkoli stav čekání, aby systém mohl vstoupit do tohoto stavu vypnutého režimu. Když se systém obnoví ze stavu spánku, služba WIA odešle WIA_EVENT_POWER_RESUME událost. V tuto chvíli musí minidriver WIA znovu obnovit stav čekání na událost přerušení. Další informace o stavech spánku naleznete v tématu Systémové stavy napájení a Stav napájení zařízení.

Doporučuje se, aby minidriver WIA uložil do mezipaměti popisovač události, který byl původně předán do metody IStiUSD::SetNotificationHandle, aby jej bylo možné znovu použít při probuzení systému z režimu spánku nebo hibernace.

Služba WIA po obnovení systému nezavolá metodu IStiUSD::SetNotificationHandle. Doporučuje se, aby minidriver volal metodu IStiUSD::SetNotificationHandle a předal popisovač události uložené v mezipaměti.

Služba WIA volá metodu IWiaMiniDrv::drvNotifyPnpEvent, když dojde k systémovým událostem. Ovladač WIA by měl zkontrolovat parametr pEventGUID a zjistit, která událost se zpracovává.

Mezi běžné události, které je potřeba zpracovat, patří:

WIA_EVENT_POWER_SUSPEND
Systém přejde do režimu pozastavení/režimu spánku.

WIA_EVENT_POWER_RESUME
Systém se probouzí z režimu pozastavení/režimu spánku.

Ovladač WIA by měl obnovit všechny stavy čekání na přerušení událostí po návratu z pozastavení. Tím zajistíte, že události budou fungovat i po probuzení systému.

Následující příklad ukazuje implementaci IWiaMiniDrv::drvNotifyPnpEvent metodou.

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