c++windowsscanningwia

Why does one of my scanners show up as a camera when querying it with Windows Image Acquisition (WIA)?


I have two scanners connected to my machine via USB (An EPSON and an HP.) The Windows Scan and Fax utility seems confident that both devices are scanners, as expected. However, when I query WIA devices on my system and print their properties in a C++ program, the HP scanner is reported as a "Camera" type. The EPSON scanner is correctly reported as a Scanner.

Here is the output of several Device Information Properties from both devices:

Device ID: {6BDD1FC6-810F-11D0-BEC7-08002BE2092F}\0000
Vendor Description: EPSON
Device Description: EPSON DS-1630
Device Type: Scanner
Device Port Name: \\.\Usbscan1
Device Name: EPSON DS-1630

Device ID: {6BDD1FC6-810F-11D0-BEC7-08002BE2092F}\0001
Vendor Description: HP
Device Description: HP ENVY 5000 (USB)
Device Type: Camera
Device Port Name: \\.\Usbscan0
Device Name: HP ENVY 5000 (USB)

windows scanner and fax util

Below is the code I'm using to dump this information:

#include <Windows.h>
#include <stdio.h>
#include <wia.h>

int main(int argc, char **argv) {
  argc;
  argv;

  HRESULT hr = CoInitialize(NULL);
  if (FAILED(hr)) {
    fprintf(stderr, "CoInitializeEx failed\n");
    goto cleanup;
  }

  IWiaDevMgr2 *dev_mgmt;
  hr = CoCreateInstance(CLSID_WiaDevMgr2, NULL, CLSCTX_LOCAL_SERVER,
                        IID_IWiaDevMgr2, (void **)&dev_mgmt);
  if (FAILED(hr)) {
    fprintf(stderr, "CoCreateInstance failed\n");
    goto cleanup;
  }

  IEnumWIA_DEV_INFO *dev_info = NULL;
  hr = dev_mgmt->EnumDeviceInfo(WIA_DEVINFO_ENUM_LOCAL, &dev_info);
  if (FAILED(hr)) {
    fprintf(stderr, "EnumDeviceInfo Failed\n");
    dev_mgmt->Release();
    goto cleanup;
  }

  while (hr == S_OK) {

    IWiaPropertyStorage *prop_storage = NULL;

    hr = dev_info->Next(1, &prop_storage, NULL);
    if (FAILED(hr)) {
      break;
    }

    if (hr == S_OK) {

      PROPSPEC prop_spec[6] = {0};
      PROPVARIANT prop_var[6] = {0};

      const ULONG prop_count = sizeof(prop_spec) / sizeof(prop_spec[0]);

      // Device ID, VT_BSTR
      prop_spec[0].ulKind = PRSPEC_PROPID;
      prop_spec[0].propid = WIA_DIP_DEV_ID;

      // Vendor Description, VT_BSTR
      prop_spec[1].ulKind = PRSPEC_PROPID;
      prop_spec[1].propid = WIA_DIP_VEND_DESC;

      // Device Description, VT_BSTR
      prop_spec[2].ulKind = PRSPEC_PROPID;
      prop_spec[2].propid = WIA_DIP_DEV_DESC;

      // Device Type, VT_I4
      prop_spec[3].ulKind = PRSPEC_PROPID;
      prop_spec[3].propid = WIA_DIP_DEV_TYPE;

      // Device Port Name, VT_BSTR
      prop_spec[4].ulKind = PRSPEC_PROPID;
      prop_spec[4].propid = WIA_DIP_PORT_NAME;

      // Device Name, VT_BSTR
      prop_spec[5].ulKind = PRSPEC_PROPID;
      prop_spec[5].propid = WIA_DIP_DEV_NAME;

      hr = prop_storage->ReadMultiple(prop_count, prop_spec, prop_var);
      if (FAILED(hr)) {
        fprintf(stderr, "prop_storage->ReadMultiple failed");
        break;
      }

      fprintf(stderr, "\nDevice ID: %S\n", prop_var[0].bstrVal);
      fprintf(stderr, "Vendor Description: %S\n", prop_var[1].bstrVal);
      fprintf(stderr, "Device Description: %S\n", prop_var[2].bstrVal);

      if (prop_var[3].iVal == 0x0000) {
        fprintf(stderr, "Device Type: Default\n");
      } else if (prop_var[3].iVal == 0x0001) {
        fprintf(stderr, "Device Type: Scanner\n");
      } else if (prop_var[3].iVal == 0x0002) {
        fprintf(stderr, "Device Type: Camera\n");
      } else if (prop_var[3].iVal == 0x0003) {
        fprintf(stderr, "Device Type: Video\n");
      } else {
        fprintf(stderr, "Device Type: Unknown (%d)\n", prop_var[3].iVal);
      }

      fprintf(stderr, "Device Port Name: %S\n", prop_var[4].bstrVal);
      fprintf(stderr, "Device Name: %S\n", prop_var[5].bstrVal);

      FreePropVariantArray(prop_count, prop_var);
      prop_storage->Release();
      prop_storage = NULL;
    }
  }

  dev_info->Release();
  dev_mgmt->Release();

cleanup:
  CoUninitialize();
}


Solution

  • I overlooked the fact that I needed to use the GET_STIDEVICE_TYPE macro to get the device type:

    Use the GET_STIDEVICE_TYPE macro to get the device type. The device type and subtype are obtained from the INF file. An application reads this property to determine whether it is using a scanner, camera, or video device.

    In which case they're now both a "Default" device type (Not quite sure what that means, however) and the devices have different SubTypes. This answers my initial question as to why it was showing up as a camera.

    Device ID: {6BDD1FC6-810F-11D0-BEC7-08002BE2092F}\0000
    Vendor Description: EPSON
    Device Description: EPSON DS-1630
    Device Type: Default (Type: 0x0000, SubType: 0x0001)
    Device Port Name: \\.\Usbscan1
    Device Name: EPSON DS-1630
    
    Device ID: {6BDD1FC6-810F-11D0-BEC7-08002BE2092F}\0001
    Vendor Description: HP
    Device Description: HP ENVY 5000 (USB)
    Device Type: Default (Type: 0x0000, SubType: 0x0002)
    Device Port Name: \\.\Usbscan0
    Device Name: HP ENVY 5000 (USB)
    

    Updated code:

    #include <Windows.h>
    #include <stdio.h>
    #include <sti.h>
    #include <wia.h>
    
    int main(int argc, char **argv) {
      argc;
      argv;
    
      HRESULT hr = CoInitialize(NULL);
      if (FAILED(hr)) {
        fprintf(stderr, "CoInitializeEx failed\n");
        goto cleanup;
      }
    
      IWiaDevMgr2 *dev_mgmt;
      hr = CoCreateInstance(CLSID_WiaDevMgr2, NULL, CLSCTX_LOCAL_SERVER,
                            IID_IWiaDevMgr2, (void **)&dev_mgmt);
      if (FAILED(hr)) {
        fprintf(stderr, "CoCreateInstance failed\n");
        goto cleanup;
      }
    
      IEnumWIA_DEV_INFO *dev_info = NULL;
      hr = dev_mgmt->EnumDeviceInfo(WIA_DEVINFO_ENUM_LOCAL, &dev_info);
      if (FAILED(hr)) {
        fprintf(stderr, "EnumDeviceInfo Failed\n");
        dev_mgmt->Release();
        goto cleanup;
      }
    
      while (hr == S_OK) {
    
        IWiaPropertyStorage *prop_storage = NULL;
    
        hr = dev_info->Next(1, &prop_storage, NULL);
        if (FAILED(hr)) {
          break;
        }
    
        if (hr == S_OK) {
    
          PROPSPEC prop_spec[6] = {0};
          PROPVARIANT prop_var[6] = {0};
    
          const ULONG prop_count = sizeof(prop_spec) / sizeof(prop_spec[0]);
    
          // Device ID, VT_BSTR
          prop_spec[0].ulKind = PRSPEC_PROPID;
          prop_spec[0].propid = WIA_DIP_DEV_ID;
    
          // Vendor Description, VT_BSTR
          prop_spec[1].ulKind = PRSPEC_PROPID;
          prop_spec[1].propid = WIA_DIP_VEND_DESC;
    
          // Device Description, VT_BSTR
          prop_spec[2].ulKind = PRSPEC_PROPID;
          prop_spec[2].propid = WIA_DIP_DEV_DESC;
    
          // Device Type, VT_I4
          prop_spec[3].ulKind = PRSPEC_PROPID;
          prop_spec[3].propid = WIA_DIP_DEV_TYPE;
    
          // Device Port Name, VT_BSTR
          prop_spec[4].ulKind = PRSPEC_PROPID;
          prop_spec[4].propid = WIA_DIP_PORT_NAME;
    
          // Device Name, VT_BSTR
          prop_spec[5].ulKind = PRSPEC_PROPID;
          prop_spec[5].propid = WIA_DIP_DEV_NAME;
    
          hr = prop_storage->ReadMultiple(prop_count, prop_spec, prop_var);
          if (FAILED(hr)) {
            fprintf(stderr, "prop_storage->ReadMultiple failed");
            break;
          }
    
          fprintf(stderr, "\nDevice ID: %S\n", prop_var[0].bstrVal);
          fprintf(stderr, "Vendor Description: %S\n", prop_var[1].bstrVal);
          fprintf(stderr, "Device Description: %S\n", prop_var[2].bstrVal);
    
          fprintf(stderr, "Device Type: ");
    
          if (GET_STIDEVICE_TYPE(prop_var[3].iVal) == 0x0000) {
            fprintf(stderr, "Default ");
          } else if (GET_STIDEVICE_TYPE(prop_var[3].iVal) == 0x0001) {
            fprintf(stderr, "Scanner ");
          } else if (GET_STIDEVICE_TYPE(prop_var[3].iVal) == 0x0002) {
            fprintf(stderr, "Camera ");
          } else if (GET_STIDEVICE_TYPE(prop_var[3].iVal) == 0x0003) {
            fprintf(stderr, "Video ");
          } else {
            fprintf(stderr, "Unknown ");
          }
    
          fprintf(stderr, "(Type: 0x%04X, SubType: 0x%04X)\n",
                  GET_STIDEVICE_TYPE(prop_var[3].iVal),
                  GET_STIDEVICE_SUBTYPE(prop_var[3].iVal));
    
          fprintf(stderr, "Device Port Name: %S\n", prop_var[4].bstrVal);
          fprintf(stderr, "Device Name: %S\n", prop_var[5].bstrVal);
    
          FreePropVariantArray(prop_count, prop_var);
          prop_storage->Release();
          prop_storage = NULL;
        }
      }
    
      dev_info->Release();
      dev_mgmt->Release();
    
    cleanup:
      CoUninitialize();
    }