Добавление поддержки событий прерываний
Чтобы правильно настроить драйвер WIA для передачи данных о событиях прерываний, сделайте следующее:
Задайте Capabilities=0x31 в INF-файле устройства. (Дополнительные сведения см. в разделе INF-файлы для устройств WIA .)
Отчет STI_GENCAP_NOTIFICATIONS и STI_USD_GENCAP_NATIVE_PUSHSUPPORT в методе IStiUSD::GetCapabilities .
Сообщите обо всех поддерживаемых событиях в методе IWiaMiniDrv::d rvGetCapabilities .
Кэшируйте и используйте дескриптор событий, переданный в методе IStiUSD::SetNotificationHandle . Это дескриптор события, который устройство сигнализирует, или мини-накопитель WIA напрямую с помощью SetEvent (описано в документации по Microsoft Windows SDK). Именно в этом методе инициируется состояние ожидания устройства WIA.
Сообщите о правильном ответе сведений о событии в методе IStiUSD::GetNotificationData .
В следующих двух примерах показана настройка устройства для прерываний с помощью реализаций методов IWiaMiniDrv::d rvGetCapabilities и IStiUSD::SetNotificationHandle .
Примечание Важно использовать перекрывающиеся вызовы ввода-вывода со всеми действиями, связанными с драйверами режима ядра. Это обеспечивает надлежащее время ожидания и отмену запросов устройств.
Объяснение реализации IWiaMiniDrv::d rvGetCapabilities
Служба WIA вызывает метод IWiaMiniDrv::d rvGetCapabilities для получения событий и команд, поддерживаемых устройством WIA. Драйвер WIA должен сначала взглянуть на входящий параметр lFlags , чтобы определить, на какой запрос он должен ответить.
Драйвер WIA должен выделить память (которая будет использоваться драйвером WIA и освобождена им) для хранения массива WIA_DEV_CAP_DRV структур. В вызове IWiaMiniDrv::d rvGetCapabilities передайте указатель на расположение памяти, которое содержит адрес памяти, выделенной драйвером WIA, в параметре ppCapabilities .
Примечание Служба 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 можно вызвать в результате одной из двух операций события.
IStiUSD::GetStatus сообщил, что ожидается событие, установив флаг STI_EVENTHANDLING_PENDING в структуре STI_DEVICE_STATUS .
Дескриптор hEvent, переданный IStiUSD::SetNotificationHandle, был передан оборудованием или путем вызова Метода 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;
}