Menu

Nakov.com logo

Thoughts on Software Engineering

Playing with the Scanner with WIA and C#

Last week I needed to implement a piece of software for scanning small documents (bank payment orders). The problem was to scan documents with specific size (less than A4) in 300 DPI and with normal brightness and contrast. I found that in C# there are two general approaches to this problem:

  • Using the TWAIN interface
  • Using Windows Image Acquisition (WIA) API

Both interfaces are not natively supported in .NET Framework so some kind of wrappers was necessary to be developed. I choose WIA because it was simpler and higher level API with sufficient enough power.

To use WIA you need to download it from Microsoft (it is a COM server, distributed freely): http://www.microsoft.com/downloads/details.aspx?familyid=a332a77a-01b8-4de6-91c2-b7ea32537e29&displaylang=en.

The next step is to add it as COM object reference in Visual Studio (you need a single file called wiaaut.dll that you need to register with regsvr32). Once you add the reference to WIA COM object in Visual Studio, you get the namespace WIA. All you need to scan image is in this namespace. The most important classes are: Device, Item and CommonDialogClass.

I will not get into more details because developers need code and usually read the text only if the code is not working. Am I right? Did you tread this text?

using System;
using System.IO;
using System.Windows.Forms;
using WIA;

namespace PlayingWithTheScanner
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        private void buttonScan_Click(object sender, EventArgs e)
        {
            CommonDialogClass commonDialogClass = new CommonDialogClass();
            Device scannerDevice = commonDialogClass.ShowSelectDevice(WiaDeviceType.ScannerDeviceType, false, false);
            if (scannerDevice != null)
            {
                Item scannnerItem = scannerDevice.Items[1];
                AdjustScannerSettings(scannnerItem, 300, 0, 0, 1010, 620, 0, 0);
                object scanResult = commonDialogClass.ShowTransfer(scannnerItem, WIA.FormatID.wiaFormatPNG, false);
                if (scanResult != null)
                {
                    ImageFile image = (ImageFile) scanResult;
                    string fileName = Path.GetTempPath() + DateTime.Now.ToString("dd-MM-yyyy-hh-mm-ss-fffffff") + ".png";
                    SaveImageToPNGFile(image, fileName);
                    pictureBoxScannedImage.ImageLocation = fileName;
                }
            }
        }

        private static void AdjustScannerSettings(IItem scannnerItem, int scanResolutionDPI, int scanStartLeftPixel, int scanStartTopPixel,
            int scanWidthPixels, int scanHeightPixels, int brightnessPercents, int contrastPercents)
        {
            const string WIA_HORIZONTAL_SCAN_RESOLUTION_DPI = "6147";
            const string WIA_VERTICAL_SCAN_RESOLUTION_DPI = "6148";
            const string WIA_HORIZONTAL_SCAN_START_PIXEL = "6149";
            const string WIA_VERTICAL_SCAN_START_PIXEL = "6150";
            const string WIA_HORIZONTAL_SCAN_SIZE_PIXELS = "6151";
            const string WIA_VERTICAL_SCAN_SIZE_PIXELS = "6152";
            const string WIA_SCAN_BRIGHTNESS_PERCENTS = "6154";
            const string WIA_SCAN_CONTRAST_PERCENTS = "6155";
            SetWIAProperty(scannnerItem.Properties, WIA_HORIZONTAL_SCAN_RESOLUTION_DPI, scanResolutionDPI);
            SetWIAProperty(scannnerItem.Properties, WIA_VERTICAL_SCAN_RESOLUTION_DPI, scanResolutionDPI);
            SetWIAProperty(scannnerItem.Properties, WIA_HORIZONTAL_SCAN_START_PIXEL, scanStartLeftPixel);
            SetWIAProperty(scannnerItem.Properties, WIA_VERTICAL_SCAN_START_PIXEL, scanStartTopPixel);
            SetWIAProperty(scannnerItem.Properties, WIA_HORIZONTAL_SCAN_SIZE_PIXELS, scanWidthPixels);
            SetWIAProperty(scannnerItem.Properties, WIA_VERTICAL_SCAN_SIZE_PIXELS, scanHeightPixels);
            SetWIAProperty(scannnerItem.Properties, WIA_SCAN_BRIGHTNESS_PERCENTS, brightnessPercents);
            SetWIAProperty(scannnerItem.Properties, WIA_SCAN_CONTRAST_PERCENTS, contrastPercents);
        }

        private static void SetWIAProperty(IProperties properties, object propName, object propValue)
        {
            Property prop = properties.get_Item(ref propName);
            prop.set_Value(ref propValue);
        }

        private static void SaveImageToPNGFile(ImageFile image, string fileName)
        {
            ImageProcess imgProcess = new ImageProcess();
            object convertFilter = "Convert";
            string convertFilterID = imgProcess.FilterInfos.get_Item(ref convertFilter).FilterID;
            imgProcess.Filters.Add(convertFilterID, 0);
            SetWIAProperty(imgProcess.Filters[imgProcess.Filters.Count].Properties, "FormatID", WIA.FormatID.wiaFormatPNG);
            image = imgProcess.Apply(image);
            image.SaveFile(fileName);
        }
    }
}

The above example scans a small area of the page in 300 DPI (color mode), saves the result in PNG format and displays it in a picture box. Download the entire example’s source code: playingwiththescanner.zip.

The interface of WIA is really ugly and is not C# friendly, but that’s the life. Enjoy….

Comments (28)

28 Responses to “Playing with the Scanner with WIA and C#”

  1. Hi Leute,
    der Code ist gut, aber unbedingt bei den Eigenschaften für WIA -> Interop-Typen einbetten auf “False” setzen, sonst werden die Klassen-Konstruktoren nicht gefunden und nichts funktioniert bzw.man bekommt Compilefehler en masse.

    Axel Arnold Bangert – Herzogenrath 2011

  2. Hi folks,
    what I am missing and searching is the grayscale option, for the default option of all scanner settings is WiaImageIntent.ColorIntent.

    Though it is implemented in -> showAcquireImage(WiaDeviceType.ScannerDeviceType,WiaImageIntent.GrayscaleIntent, WiaImageBias.MinimizeSize, WIA.FormatID.wiaFormatJPEG, false, true, false) -> but I don’t want to see the Scanner GUI.

    So there must be something like -> ?????const string WIA_SCAN_GrayscaleIntent = ??????;
    Does someone know something about that?

    Axel Arnold Bangert – Herzogenrath 2011

  3. Hi folks,
    what I am missing and searching is the grayscale option, for the default option of all scanner settings is WiaImageIntent.ColorIntent.

    Though it is implemented in -> showAcquireImage(WiaDeviceType.ScannerDeviceType,WiaImageIntent.GrayscaleIntent, WiaImageBias.MinimizeSize, WIA.FormatID.wiaFormatJPEG, false, true, false) -> but I don’t want to see the Scanner GUI.

    So there must be something like -> ?????const string WIA_SCAN_GrayscaleIntent = ??????;
    Does someone know something about that?

    Axel Arnold Bangert – Herzogenrath 2011

  4. Hi,
    I have just found it here http://msdn.microsoft.com/en-us/library/ms630196%28v=VS.85%29.aspx

    WIA_IPS_CUR_INTENT — WIA_INTENT_IMAGE_TYPE_GRAYSCALE

    Axel Arnold Bangert – Herzogenrath 2011

  5. Hi,
    it works fine for grayscale like that:AdjustScannerSettings(scannnerItem, 150, 0, 0, 1275, 1755, 2, 0, 0);…private static void AdjustScannerSettings(IItem scannnerItem, int scanResolutionDPI,int scanStartLeftPixel, int scanStartTopPixel,int scanWidthPixels,
    int scanHeightPixels, int scanColorIntent, int brightnessPercents, int contrastPercents)…const string WIA_IPS_CUR_INTENT = “6146”;SetWIAProperty(scannnerItem.Properties, WIA_IPS_CUR_INTENT, scanColorIntent);…
    Axel Arnold Bangert – Herzogenrath 2011

  6. Grayscale
    Axel Arnold Bangert – Herzogenrath 2011

  7. Hi
    wiothout the right PropertyID nothing works. To get the right PropertyID you can
    catch it like that:
    for (int i = 1 ; i < scannnerItem.Properties.Count; i++)
    {
    Property prop = scannnerItem.Properties.get_Item(i);
    MessageBox.Show(prop.Name.ToString() + " = " + prop.PropertyID.ToString());
    }
    Without the right PropertyID you cannot set the constant – for example:
    const string WIA_IPS_CUR_INTENT = “6146″

    Best regards
    Axel Arnold Bangert – Herzogenrath

  8. Item Name = 4098
    Full Item Name = 4099
    Item Flags = 4101
    Color Profile Name = 4120
    Brightness = 6154
    Contrast = 6155
    Private Highlight Level = 71692
    Private Midtone Level = 71694
    Private Shadow Level = 71693
    Private Gamma = 71695
    Private Saturation = 71699
    Private Hue X = 71696
    Private Hue Y = 71697
    Private Sharpen Level = 71698
    Threshold = 6159
    Horizontal Resolution = 6147
    Vertical Resolution = 6148
    Private Default Resolution = 71687
    Private Quality Resolution = 71688
    Horizontal Start Position = 6149
    Vertical Start Position = 6150
    Horizontal Extent = 6151
    Vertical Extent = 6152
    Pixels Per Line = 4112
    Bytes Per Line = 4113
    Number of Lines = 4114
    Item Size = 4116
    Minimum Buffer Size = 4118
    Current Intent = 6146
    Data Type = 4103
    Bits Per Pixel = 4104
    Bits Per Channel = 4110
    Channels Per Pixel = 4109
    Planar = 4111
    Compression = 4107
    Media Type = 4108
    Format = 4106
    Preferred Format = 4105
    Filename extension = 4123
    Access Rights = 4102
    Photometric Interpretation = 6153
    Private Source Depth = 71686
    Private Preview = 71683
    Private Exposure Method = 71689
    Private Smoothing = 71722
    Private Color Enhanced = 71723
    Private TMA Method = 71685
    Private Defaults = 71701

    Axel Arnold Bangert – Herzogenrath 2011

  9. const DeviceID = 2
    const Manufacturer = 3
    const Description = 4
    const Type = 5
    const Port = 6
    const Name = 7
    const Server = 8
    const RemoteDevID = 9
    const UIClassID = 10
    const FirmwareVersion = 1026
    const ConnectStatus = 1027
    const DeviceTime = 1028
    const PicturesTaken = 2050
    const PicturesRemaining = 2051
    const ExposureMode = 2052
    const ExposureCompensation = 2053
    const ExposureTime = 2054
    const FNumber = 2055
    const FlashMode = 2056
    const FocusMode = 2057
    const FocusManualDist = 2058
    const ZoomPosition = 2059
    const PanPosition = 2060
    const TiltPostion = 2061
    const TimerMode = 2062
    const TimerValue = 2063
    const PowerMode = 2064
    const BatteryStatus = 2065
    const Dimension = 2070
    const HorizontalBedSize = 3074
    const VerticalBedSize = 3075
    const HorizontalSheetFeedSize = 3076
    const VerticalSheetFeedSize = 3077
    const SheetFeederRegistration = 3078
    const HorizontalBedRegistration = 3079
    const VerticalBedRegistraion = 3080
    const PlatenColor = 3081
    const PadColor = 3082
    const FilterSelect = 3083
    const DitherSelect = 3084
    const DitherPatternData = 3085
    const DocumentHandlingCapabilities = 3086
    const DocumentHandlingStatus = 3087
    const DocumentHandlingSelect = 3088
    const DocumentHandlingCapacity = 3089
    const HorizontalOpticalResolution = 3090
    const VerticalOpticalResolution = 3091
    const EndorserCharacters = 3092
    const EndorserString = 3093
    const ScanAheadPages = 3094
    const MaxScanTime = 3095
    const Pages = 3096
    const PageSize = 3097
    const PageWidth = 3098
    const PageHeight = 3099
    const Preview = 3100
    const TransparencyAdapter = 3101
    const TransparecnyAdapterSelect = 3102
    const ItemName = 4098
    const FullItemName = 4099
    const ItemTimeStamp = 4100
    const ItemFlags = 4101
    const AccessRights = 4102
    const DataType = 4103
    const BitsPerPixel = 4104
    const PreferredFormat = 4105
    const Format = 4106
    const Compression = 4107
    const MediaType = 4108
    const ChannelsPerPixel = 4109
    const BitsPerChannel = 4110
    const Planar = 4111
    const PixelsPerLine = 4112
    const BytesPerLine = 4113
    const NumberOfLines = 4114
    const GammaCurves = 4115
    const ItemSize = 4116
    const ColorProfiles = 4117
    const BufferSize = 4118
    const RegionType = 4119
    const ColorProfileName = 4120
    const ApplicationAppliesColorMapping = 4121
    const StreamCompatibilityID = 4122
    const ThumbData = 5122
    const ThumbWidth = 5123
    const ThumbHeight = 5124
    const AudioAvailable = 5125
    const AudioFormat = 5126
    const AudioData = 5127
    const PicturesPerRow = 5128
    const SequenceNumber = 5129
    const TimeDelay = 5130
    const CurrentIntent = 6146
    const HorizontalResolution = 6147
    const VerticalResolution = 6148
    const HorizontalStartPostion = 6149
    const VerticalStartPosition = 6150
    const HorizontalExtent = 6151
    const VerticalExtent = 6152
    const PhotometricInterpretation = 6153
    const Brightness = 6154
    const Contrast = 6155
    const Orientation = 6156
    const Rotation = 6157
    const Mirror = 6158
    const Threshold = 6159
    const Invert= 6160
    const LampWarmUpTime = 6161

  10. Nakov says:

    In fact WIA is less flexible than TWAIN. Not all options are controllable and generally scanning is slower than with TWAIN. It works for simple tasks but is not good enough in more complex scenarios.

  11. chandrasekhar says:

    Hi every one,

    This example is working fine but in preview of document the document is not displaying full.

    How to change the settings to preview full image of a document in this ………

    Please help me ………

  12. nakov says:

    WIA has missing features and works slower. For more complex applications use the TWAIN API.

  13. weimar says:

    Hello good example you gave.

    I need your help, I get the following error.

    CommonDialogClass commonDialogClass = new CommonDialogClass ();

    Interop type ‘WIA.CommonDialogClass’ can not be embedded. Use the applicable interface instead.

    please if anyone knows thanks in advance resolve.

  14. Zeeshan says:

    Great post, thats what I needed.

    @weimar:
    Just double click on reference on Solution Explorer and Select WIA, then in properties Window, select [Embed Interop Type] property and make it false.

  15. Anonymous says:

    Sorry my Keyboard is not working properly, thats y typo. Great post and its really useful for me since Iam a beginner.

  16. Chirag says:

    I have uploaded SOlution but found below error.

    Could not load file or assembly ‘Interop.WIA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=a5a1b2327382927a’ or one of its dependencies. The system cannot find the file specified.

    Please Advice me as soon as possible

    • nakov says:

      Maybe the version of .NET Framework. This codee is very old. You need the assembly Interop.WIA.dll, version 1.0. Try to find is somewhere in Internet.

  17. nakov says:

    In Windows 7 and Windows 8, WIA is part of Windows and no additional download is required. You can just add a reference to the COM server %WinDir%\System32\wiaaut.dll from Visual Studio. Visual Studio will make automatically Interop assembly. You can choose “Windows Image Acquisition Library v2.0” from the “COM” section in the “Reference Manager” in Visual Studio (right click on the project –> Add Reference…)

  18. aryan says:

    How to implement the same in asp.net web application?any idea plz share…..

    Thanks in advance….

  19. aryan says:

    If we are using third party tool i.e TWAIN,we have to buy it.Is there any way by which we can make it for free.If then plz share

    Thanks in advance…..

  20. Lorraine says:

    I’ll try to put this to good use imemeiatdly.

  21. Kamses says:

    good website found here.

  22. Ahmedkhan9 says:

    In Windows 7 to Windows 10 WIA is part of Windows and no additional download is required. Now in this article you have showed the relation of WIA and C#. You may take a look of https://procpedia.com/wia-error-codes which help to learn more about WIA errors codes which we face and their solution as well.

  23. Wilson says:

    Has anybody been able to do a duplex scan using a document feeder using WIA 2.0. Am able to scan but onnly front page is acquired.

    • Vision says:

      In order to scan both sides, a property may have to be set. This setting is made on the device, not on the item. This could look like this, for example:

      if (duplex)
      {
      SetProp(device, WIA_PROPERTIES.WIA_DPS_DOCUMENT_HANDLING_SELECT, WIA_PROPERTIES.WIA_ADF_DUPLEX);
      }

      The following applies:
      private class WIA_PROPERTIES
      {
      public const uint WIA_ADF_DUPLEX = 0x005;
      public const and WIA_DPS_DOCUMENT_HANDLING_SELECT = 3088;
      }

      And the method that sets the property:

      private void SetProp(WIA.Device device, uint target, object value)
      {
      if (device == null)
      return;

      if (target == null)
      return;

      foreach (WIA.Property prop in device.Properties)
      {
      if (prop.PropertyID == target)
      prop.set_Value(value);
      }
      }

      Translated with http://www.DeepL.com/Translator

  24. Mamun says:

    How I can preview simultaneously before final scan?

  25. Parveen says:

    I used this code but not getting output properly, just getting some dots in middle of the page expect that everything is white any help pls.

RSS feed for comments on this post. TrackBack URL

LEAVE A COMMENT