Using a scanner without dialogs in .Net

I recently had the task to create a sort of a web-interface for a scanner device (amongst other devices). The project is to be done in C# (Microsoft .Net Framework 4.0), so I had many helpful examples on how to do this, either by TWAIN or WIA libraries. Unfortunately all of these examples had one common problem, interrupting dialogs, either for scanning device selection, or the scanning options dialog, which were necessary to make the scan. As this is a web-interface, user will not be seeing any of these dialogs, so displaying them did just blocked the whole process.

After much searching I finally found an actually useful post on Windows Image Acquisition, which by the way also had a blocking dialog, but this was easily modified and in moments I had a simple class with a Scan(string scannerId) function which would do just that, Scan with a selected device and nothing more.

WIAScanner Class Source (C#)

using System;
using System.Collections.Generic;
using System.IO;
using System.Drawing;

namespace WIATest
{
    class WIAScanner
    {
        const string wiaFormatBMP = "{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}";

        class WIA_DPS_DOCUMENT_HANDLING_SELECT
        {
            public const uint FEEDER = 0x00000001;
            public const uint FLATBED = 0x00000002;
        }

        class WIA_DPS_DOCUMENT_HANDLING_STATUS
        {
            public const uint FEED_READY = 0x00000001;
        }

        class WIA_PROPERTIES
        {
            public const uint WIA_RESERVED_FOR_NEW_PROPS = 1024;
            public const uint WIA_DIP_FIRST = 2;
            public const uint WIA_DPA_FIRST = WIA_DIP_FIRST + WIA_RESERVED_FOR_NEW_PROPS;
            public const uint WIA_DPC_FIRST = WIA_DPA_FIRST + WIA_RESERVED_FOR_NEW_PROPS;
            //
            // Scanner only device properties (DPS)
            //
            public const uint WIA_DPS_FIRST = WIA_DPC_FIRST + WIA_RESERVED_FOR_NEW_PROPS;
            public const uint WIA_DPS_DOCUMENT_HANDLING_STATUS = WIA_DPS_FIRST + 13;
            public const uint WIA_DPS_DOCUMENT_HANDLING_SELECT = WIA_DPS_FIRST + 14;
        }

        /// <summary>
        /// Use scanner to scan an image (with user selecting the scanner from a dialog).
        /// </summary>
        /// <returns>Scanned images.</returns>
        public static List<Image> Scan()
        {
            WIA.ICommonDialog dialog = new WIA.CommonDialog();
            WIA.Device device = dialog.ShowSelectDevice(WIA.WiaDeviceType.UnspecifiedDeviceType, true, false);

            if (device != null)
            {
                return Scan(device.DeviceID);
            }
            else
            {
                throw new Exception("You must select a device for scanning.");
            }
        }

        /// <summary>
        /// Use scanner to scan an image (scanner is selected by its unique id).
        /// </summary>
        /// <param name="scannerName"></param>
        /// <returns>Scanned images.</returns>
        public static List<Image> Scan(string scannerId)
        {
            List<Image> images = new List<Image>();

            bool hasMorePages = true;
            while (hasMorePages)
            {
                // select the correct scanner using the provided scannerId parameter
                WIA.DeviceManager manager = new WIA.DeviceManager();
                WIA.Device device = null;
                foreach (WIA.DeviceInfo info in manager.DeviceInfos)
                {
                    if (info.DeviceID == scannerId)
                    {
                        // connect to scanner
                        device = info.Connect();
                        break;
                    }
                }

                // device was not found
                if (device == null)
                {
                    // enumerate available devices
                    string availableDevices = "";
                    foreach (WIA.DeviceInfo info in manager.DeviceInfos)
                    {
                        availableDevices += info.DeviceID + "n";
                    }

                    // show error with available devices
                    throw new Exception("The device with provided ID could not be found. Available Devices:n" + availableDevices);
                }

                WIA.Item item = device.Items[1] as WIA.Item;

                try
                {
                    // scan image
                    WIA.ICommonDialog wiaCommonDialog = new WIA.CommonDialog();
                    WIA.ImageFile image = (WIA.ImageFile)wiaCommonDialog.ShowTransfer(item, wiaFormatBMP, false);

                    // save to temp file
                    string fileName = Path.GetTempFileName();
                    File.Delete(fileName);
                    image.SaveFile(fileName);
                    image = null;

                    // add file to output list
                    images.Add(Image.FromFile(fileName));
                }
                catch (Exception exc)
                {
                    throw exc;
                }
                finally
                {
                    item = null;

                    //determine if there are any more pages waiting
                    WIA.Property documentHandlingSelect = null;
                    WIA.Property documentHandlingStatus = null;

                    foreach (WIA.Property prop in device.Properties)
                    {
                        if (prop.PropertyID == WIA_PROPERTIES.WIA_DPS_DOCUMENT_HANDLING_SELECT)
                            documentHandlingSelect = prop;

                        if (prop.PropertyID == WIA_PROPERTIES.WIA_DPS_DOCUMENT_HANDLING_STATUS)
                            documentHandlingStatus = prop;
                    }

                    // assume there are no more pages
                    hasMorePages = false;

                    // may not exist on flatbed scanner but required for feeder
                    if (documentHandlingSelect != null)
                    {
                        // check for document feeder
                        if ((Convert.ToUInt32(documentHandlingSelect.get_Value()) &amp;amp;amp;amp; WIA_DPS_DOCUMENT_HANDLING_SELECT.FEEDER) != 0)
                        {
                            hasMorePages = ((Convert.ToUInt32(documentHandlingStatus.get_Value()) &amp;amp;amp;amp; WIA_DPS_DOCUMENT_HANDLING_STATUS.FEED_READY) != 0);
                        }
                    }
                }
            }

            return images;
        }

        /// <summary>
        /// Gets the list of available WIA devices.
        /// </summary>
        /// <returns></returns>
        public static List<string> GetDevices()
        {
            List<string> devices = new List<string>();
            WIA.DeviceManager manager = new WIA.DeviceManager();

            foreach (WIA.DeviceInfo info in manager.DeviceInfos)
            {
                devices.Add(info.DeviceID);
            }

            return devices;
        }

    }
}

Example application for scanning without dialogs

This application is pretty simple, main focus is on the WIAScanner class which provides scanning ability to the user either by selecting the device manually (trough a dialog), or programatically (from configuration, hard-code…). Before you try the example, you will need the WIA 2.0 library:

  1. download it from here
  2. copy the wiaaut.dll to your system folder (c:windowssystem32)
  3. register the component by typing “regsvr32 wiaaut.dll”
  4. that’s it

You can download the example application here:

9 thoughts on “Using a scanner without dialogs in .Net

  1. Thanks for the sample code, Miljenko! This is very timely, as I’ve just started developing a scanner applet that wouldn’t use the standard WIA dialogs. Your code worked well for me, and it was straightforward to read.

  2. I’m glad you find it useful. I myself lost a day or two to figure out how to use WIA scanner without dialogs. The Web is full of articles and examples on how to use it, but none are without dialogs. Anyway, enjoy…

    • Interesting.

      This code will be functionally in Asp.Net?

      Thank’s

      • I haven’t tried it, but it should work in ASP.Net application, just use the same logic as in the example, get a list of devices, select a device and scan using the “public static List Scan(string scannerId)” function.

  3. Thanks a lot man, your code works well and saved me a lot of time.

  4. hello, I’ve been trying to run your code but after it starts scanning I get an error message which says
    generic gdi+ error. I’ve been trying to fix this but I have not found a way how, do you know why this shows up?

    • I’m sorry, but I haven’t encountered any such errors. Perhaps the problem is in the device itself, does it support WIA 2.0?