加载和卸载 WIA 微型驱动程序

安装 WIA 设备驱动程序后,WIA 服务将首次尝试加载它。 将调用 WIA 微型驱动程序的 IStiUSD::Initialize 方法,应执行以下任务:

  1. 检查传输模式以确定调用方初始化此设备驱动程序的意向。 这是通过调用 IStiDeviceControl::GetMyDeviceOpenMode 方法完成的。

  2. 获取已安装设备的端口名称,以便此驱动程序可以在正确的端口上调用 Microsoft Windows SDK) 中所述的 CreateFile (来访问设备。 这是通过调用 IStiDeviceControl::GetMyDevicePortName 方法完成的。

  3. 读取设备安装期间编写的特定于设备的注册表设置。 这可以通过使用传递给 IStiUSD::InitializehParametersKey 参数来完成。

首次加载驱动程序时,WIA 服务会调用 IStiUSD::Initialize 方法。 当客户端使用旧版 STI DDI 并调用 IStillImage::CreateDevice 方法时,也会调用 IStiUSD::Initialize 方法。

IStiUSD::Initialize 方法应初始化 WIA 驱动程序和设备以供使用。 WIA 驱动程序可以存储 IStiDeviceControl 接口指针(如果以后需要该指针)。 在存储此接口之前,必须调用 IStiDeviceControl::AddRef 方法。 如果不需要存储接口,请忽略它。 如果尚未首先调用 IStiDeviceControl::AddRef,请不要释放IStiDeviceControl 接口。 这可能会导致不可预知的结果。 需要 IStiDeviceControl COM 接口 来获取有关设备端口的信息。 调用 CreateFile 函数时使用的端口名称可以通过调用 IStiDeviceControl::GetMyDevicePortName 方法获取。 对于共享端口上的设备(例如串行端口设备),不建议在 IStiUSD::Initialize 中打开端口。 只能在调用 IStiUSD::LockDevice 时打开端口。 应在内部控制端口关闭以提供快速访问。 (在 IStiUSD::LockDeviceIStiUSD::UnLockDevice 中打开和关闭非常低效。 CreateFile 可能会导致延迟,使设备看起来很慢且对用户无响应。)

如果 WIA 驱动程序不能在同一设备端口上支持多个 CreateFile 调用,则应调用 IStiDeviceControl::GetMyDeviceOpenMode 方法。

WIA 驱动程序应检查STI_DEVICE_CREATE_DATA标志返回的模式值,并相应地打开端口。

如果必须打开设备端口,则应使用对 CreateFile 的 调用。 打开端口时,应使用 FILE_FLAG_OVERLAPPED 标志。 这允许在访问设备时使用 Windows SDK 文档) 中所述的 OVERLAPPED 结构 (。 使用重叠 I/O 有助于控制对硬件的响应式访问。 检测到问题时,WIA 驱动程序可以调用 Windows SDK 文档) 中所述的 CancelIo (停止所有当前硬件访问。

以下示例演示 IStiUSD::Initialize 方法的实现。

STDMETHODIMP CWIADevice::Initialize(
  PSTIDEVICECONTROL   pIStiDeviceControl,
  DWORD               dwStiVersion,
  HKEY                hParametersKey)
{
  if (!pIStiDeviceControl) {
      return STIERR_INVALID_PARAM;
  }

  HRESULT hr = S_OK;

  //
  // Get the mode of the device to check why we were created.  status, data, or both...
  //

  DWORD dwMode = 0;
  hr = pIStiDeviceControl->GetMyDeviceOpenMode(&dwMode);
  if(FAILED(hr)){
      return hr;
  }

  if(dwMode & STI_DEVICE_CREATE_DATA)
  {
      //
      // device is being opened for data
      //
  }

  if(dwMode & STI_DEVICE_CREATE_STATUS)
  {
      //
      // device is being opened for status
      //
  }

  if(dwMode & STI_DEVICE_CREATE_BOTH)
  {
      //
      // device is being opened for both data and status
      //
  }

  //
  // Get the name of the device port to be used in a call to CreateFile().
  //

  WCHAR szDevicePortNameW[MAX_PATH];
  memset(szDevicePortNameW,0,sizeof(szDevicePortNameW));

  hr = pIStiDeviceControl->GetMyDevicePortName(szDevicePortNameW,
                                            sizeof(szDevicePortNameW)/sizeof(WCHAR));
  if(FAILED(hr)) {
      return hr;
  }

  //
  // Open kernel-mode device driver. Use the FILE_FLAG_OVERLAPPED flag 
  // for proper cancellation
  // of kernel-mode operations and asynchronous file IO. 
  //  The CancelIo() call will function properly if this flag is used.
  //  It is recommended to use this flag.
  //

  m_hDeviceDataHandle = CreateFileW(szDevicePortNameW,
                                   GENERIC_READ | GENERIC_WRITE, // Access mask
                                   0,                            // Share mode
                NULL,                         // SA
                                   OPEN_EXISTING,                // Create disposition
                                   FILE_ATTRIBUTE_SYSTEM|FILE_FLAG_OVERLAPPED,
                                   NULL );

  m_dwLastOperationError = ::GetLastError();

  hr = (m_hDeviceDataHandle != INVALID_HANDLE_VALUE) ?
              S_OK : MAKE_HRESULT(SEVERITY_ERROR,FACILITY_WIN32,m_dwLastOperationError);

  if (FAILED(hr)) {
      return hr;
  }

  //
  // Open DeviceData section to read driver specific information
  //

  HKEY hKey = hParametersKey;
  HKEY hOpenKey = NULL;
  if (RegOpenKeyEx(hKey,                     // handle to open key
                   TEXT("DeviceData"),       // address of name of subkey to open
                   0,                        // options (must be NULL)
                   KEY_QUERY_VALUE|KEY_READ, // just want to QUERY a value
                   &hOpenKey                 // address of handle to open key
     ) == ERROR_SUCCESS) {

      //
      // This is where you read registry entries for your device.
      // The DeviceData section is the proper place to put this 
      // information. Information about your device should
      // have been written using the WIA device's .INF installation
      // file.
      // You can access this information from this location in the
      // Registry. The WIA service owns the hParameters HKEY. 
      // DO NOT CLOSE THIS KEY. Always close any HKEYS opened by
      //  this WIA driver after you are finished.
      //

      //
      // close registry key when finished, reading DeviceData information.
      //

      RegCloseKey(hOpenKey);
  } else {
      return E_FAIL;
  }
  return hr;
}

WIA 服务在成功调用 IStiUSD:: Initialize 方法后调用 IStiUSD:: GetCapabilities。 然后,IStiUSD::GetCapabilitiesSTI_USD_CAPS 结构提供 STI 版本信息、WIA 支持标志 (位标志,指示驱动程序功能) 以及任何事件要求。

以下示例演示 IStiUSD::GetCapabilities 的实现。

/********************************************************************\
* CWIADevice::GetCapabilities
* Remarks:
* This WIA driver sets the following capability flags:
* 1. STI_GENCAP_WIA - This driver supports WIA
* 2. STI_USD_GENCAP_NATIVE_PUSHSUPPORT - This driver supports push
*    buttons
* 3. STI_GENCAP_NOTIFICATIONS - This driver requires the use of 
*    interrupt events.
*
\********************************************************************/

STDMETHODIMP CWIADevice::GetCapabilities(PSTI_USD_CAPS pUsdCaps)
{
  //
  // If the caller did not pass in the correct parameters,
  // then fail the call with E_INVALIDARG.
  //

  if (!pUsdCaps) {
      return E_INVALIDARG;
  }

  memset(pUsdCaps, 0, sizeof(STI_USD_CAPS));
  pUsdCaps->dwVersion     = STI_VERSION;    // STI version
  pUsdCaps->dwGenericCaps = STI_GENCAP_WIA| // WIA support
                            STI_USD_GENCAP_NATIVE_PUSHSUPPORT| // button support
                            STI_GENCAP_NOTIFICATIONS; // interrupt event support
  return S_OK;
}