添加中断事件支持

若要正确设置 WIA 驱动程序以报告中断事件,请执行以下操作:

  1. 在设备的 INF 文件中设置 Capabilities=0x31 。 (有关详细信息,请参阅 WIA 设备的 INF 文件 。)

  2. IStiUSD::GetCapabilities 方法中报告STI_GENCAP_NOTIFICATIONS和STI_USD_GENCAP_NATIVE_PUSHSUPPORT。

  3. 报告 IWiaMiniDrv::d rvGetCapabilities 方法中支持的所有事件。

  4. 缓存并使用 IStiUSD::SetNotificationHandle 方法中传递的事件句柄。 这是设备发出信号的事件句柄,或 WIA 微型驱动程序直接使用 setEvent (发出信号的事件句柄,如Microsoft Windows SDK文档) 中所述。 正是在此方法中启动 WIA 设备的等待状态。

  5. IStiUSD::GetNotificationData 方法中报告正确的事件信息响应。

以下两个示例演示如何使用 IWiaMiniDrv::d rvGetCapabilitiesIStiUSD::SetNotificationHandle 方法的实现来配置设备以用于中断。

注意 请务必将重叠的 I/O 调用用于涉及内核模式驱动程序的所有活动。 这允许适当的超时和取消设备请求。

IWiaMiniDrv::d rvGetCapabilities 实现的说明

WIA 服务调用 IWiaMiniDrv::d rvGetCapabilities 方法以获取 WIA 设备支持的事件和命令。 WIA 驱动程序应首先查看传入 的 lFlags 参数,以确定它应回答哪个请求。

WIA 驱动程序应分配内存 (供 WIA 驱动程序使用,并由其释放,) 包含 WIA_DEV_CAP_DRV 结构数组。 在调用 IWiaMiniDrv::d rvGetCapabilities 时,传递指向内存位置的指针,该位置保存 ppCapabilities 参数中 WIA 驱动程序分配的内存的地址。

注意 WIA 服务不会释放此内存。 WIA 驱动程序管理分配的内存非常重要。

以下示例演示 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;
}

IStiUSD::SetNotificationHandle 方法由 WIA 服务或此驱动程序在内部调用,以启动或停止事件通知。 WIA 服务将传入使用 createEvent (创建的有效句柄(如Microsoft Windows SDK文档) 中所述),指示 WIA 驱动程序将在硬件中发生事件时发出此句柄的信号。

NULL 可以传递给 IStiUSD::SetNotificationHandle 方法。 NULL 指示 WIA 微型驱动程序将停止所有设备活动,并退出任何事件等待操作。

以下示例演示 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;
}

当 WIA 微型驱动程序或 WIA 设备检测到事件并发出信号时,WIA 服务将调用 IStiUSD::GetNotificationData 方法。 WIA 微型驱动程序应在此方法中报告所发生事件的详细信息。

WIA 服务调用 IStiUSD::GetNotificationData 方法以获取有关刚刚发出信号的事件的信息。 可以通过两个事件操作之一调用 IStiUSD::GetNotificationData 方法。

  1. IStiUSD::GetStatus 通过在 STI_DEVICE_STATUS 结构中设置 STI_EVENTHANDLING_PENDING 标志来报告存在挂起的事件。

  2. 通过 IStiUSD::SetNotificationHandle 传入的 hEvent 句柄由硬件发出信号,或通过调用 Microsoft Windows SDK 文档) 中所述的 SetEvent (发出信号。

WIA 驱动程序负责填写 STINOTIFY 结构

以下示例演示 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;
}

通过将 NULL 作为事件句柄传递 可以随时停止中断事件。 微型驱动程序应将此解释为停止硬件设备上任何等待状态的信号。

IWiaMiniDrv::d rvNotifyPnpEvent 方法可以接收影响事件等待状态的电源管理事件。

WIA 服务调用 IWiaMiniDrv::d rvNotifyPnpEvent 方法,并在系统即将处于睡眠状态时发送WIA_EVENT_POWER_SUSPEND事件。 如果发生此调用,则设备可能已退出其等待状态。 睡眠状态会自动触发内核模式驱动程序退出任何等待状态,以允许系统进入此关闭状态。 当系统从其睡眠状态恢复时,WIA 服务将发送WIA_EVENT_POWER_RESUME事件。 此时,WIA 微型驱动程序必须重新建立中断事件等待状态。 有关睡眠状态的详细信息,请参阅系统电源状态和设备电源状态

建议 WIA 微型驱动程序缓存最初传递到 IStiUSD::SetNotificationHandle 方法的事件句柄,以便在系统从睡眠或休眠状态唤醒时重复使用它。

系统恢复后,WIA 服务 不会 调用 IStiUSD::SetNotificationHandle 方法。 建议微型驱动程序调用其 IStiUSD::SetNotificationHandle 方法,传递缓存的事件句柄。

发生系统事件时,WIA 服务调用 IWiaMiniDrv::d rvNotifyPnpEvent 方法。 WIA 驱动程序应检查 pEventGUID 参数来确定正在处理的事件。

必须处理的一些常见事件包括:

WIA_EVENT_POWER_SUSPEND
系统将进入挂起/睡眠模式。

WIA_EVENT_POWER_RESUME
系统正在从暂停/睡眠模式中唤醒。

WIA 驱动程序应在从挂起返回后还原任何事件中断等待状态。 这可确保事件在系统唤醒时仍然有效。

以下示例演示 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;
}