添加中断事件支持
若要正确设置 WIA 驱动程序以报告中断事件,请执行以下操作:
在设备的 INF 文件中设置 Capabilities=0x31 。 (有关详细信息,请参阅 WIA 设备的 INF 文件 。)
在 IStiUSD::GetCapabilities 方法中报告STI_GENCAP_NOTIFICATIONS和STI_USD_GENCAP_NATIVE_PUSHSUPPORT。
报告 IWiaMiniDrv::d rvGetCapabilities 方法中支持的所有事件。
缓存并使用 IStiUSD::SetNotificationHandle 方法中传递的事件句柄。 这是设备发出信号的事件句柄,或 WIA 微型驱动程序直接使用 setEvent (发出信号的事件句柄,如Microsoft Windows SDK文档) 中所述。 正是在此方法中启动 WIA 设备的等待状态。
在 IStiUSD::GetNotificationData 方法中报告正确的事件信息响应。
以下两个示例演示如何使用 IWiaMiniDrv::d rvGetCapabilities 和 IStiUSD::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 方法。
IStiUSD::GetStatus 通过在 STI_DEVICE_STATUS 结构中设置 STI_EVENTHANDLING_PENDING 标志来报告存在挂起的事件。
通过 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;
}