다음을 통해 공유


인터럽트 이벤트 지원 추가

인터럽트 이벤트를 보고하도록 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 메서드에 전달된 이벤트 핸들을 캐시하고 사용합니다. 디바이스가 신호를 전송하는 이벤트 핸들이거나, SetEvent를 사용하여 WIA 미니드라이버가 직접 신호를 보냅니다(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_DEV_CAP_DRV 구조의 배열을 포함하도록 메모리를 할당해야 합니다(WIA 드라이버에서 사용 및 해제). 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 드라이버가 하드웨어에서 이벤트가 발생할 때 이 핸들에 신호를 보낼 것임을 나타냅니다.

NULLIStiUSD::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::GetStatusSTI_DEVICE_STATUS 구조에서 STI_EVENTHANDLING_PENDING 플래그를 설정하여 보류 중인 이벤트가 있다고 보고했습니다.

  2. IStiUSD::SetNotificationHandle에서 전달된 hEvent 핸들은 하드웨어에서 또는 SetEvent를 호출하여 신호를 받았습니다(Microsoft Windows SDK 설명서에 설명됨).

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