Skip to content
October 19, 2011 / Andrew

Scanning All Pages in a Document Feeder with Silverlight

Problem

The Silverlight app I’m working on needs to be able to scan a document from a scanner’s automatic document feeder (ADF) with a single button click, and then attach the images to a business object. There are some samples online – some are for Silverlight, and some talk to an ADF, but I found none that fit both criteria.

Solution

I learned a lot about WIA and COM automation. I read an excellent article about scanning in Silverlight, written by Pete Brown of Microsoft; I highly recommend you read it too if you haven’t already. I took some pieces from this CodeGuru forum post about non-Silverlight ADF scanning. I sifted through WIA documentation and property definitions and even some WIA source code. The end result was a class I (very creatively) called WiaScanner.

WiaScanner has one method: ScanAllPages. You tell this method what to do with the byte array of each page, e.g. load into memory or save to disk.

private List<byte[]> _pages;

public void StartScanning()
{
    this._pages = new List<byte[]>();
    var scanner = new WiaScanner();
    scanner.ScanAllPages(imageBytes => this._pages.Add(imageBytes));
    Console.WriteLine("Scanned {0} pages.", this._pages.Count);
}

This action is executed synchronously as soon as each page is acquired. That is, page 1 is acquired, and page 1 is processed by your method. Then page 2 is acquired, and so on.

WiaScanner works with flatbeds and automatic document feeders. If a scanner has both a flatbed and a feeder, and the scanner detects a document in the feeder, it scans from the feeder. If no document is loaded in the feeder, it scans from the flatbed.

It acquires images in PNG format because that’s what my application needed. If you need more flexibility, I’m sure you can easily adapt the code to meet your needs. (Of course Silverlight only supports PNG and JPEG.)

Note also that WiaScanner is hard-coded to scan in black and white, but obviously you could change that too. Neither grayscale nor color were desired for the app I was writing.

Every scanner is different, so I can’t guarantee that WiaScanner works with all of them. Hopefully it will at least get you close!

A Peek Under The Hood, If You’re Interested

COM automation involves extensive use of dynamic types. I chose to encapsulate all the dynamic stuff inside private classes to hide all of that from consumers of WiaScanner.

Each WIA device object and item (read: “image”) object has a collection of properties, each of which has an ID defined in wiadef.h. You configure a scan by setting these properties. This was a little funky, since different devices support different sets of properties. So while the DocumentHandlingStatus property always has an ID of 3087, it might be at index 2 for one device and index 5 for another. Once WiaScanner finds a property, it remembers where it was for next time so it doesn’t have to do repeated linear searches.

There’s another parameter you can pass to ScanAllPages that will cause the common WIA dialog to show up. This allows the user to set things like DPI and color depth, without you having to code up that UI. However it gives less programmatic control over the scanning process. Only the first page will be scanned from a feeder, and often my tests resulted in a hung application. 😦 So I wouldn’t mess with that unless you know you’re dealing with a flatbed.

Here’s the weirdest issue I came across. For some reason I couldn’t get my flatbed to scan the entire image in some situations. I don’t remember everything I tried, but finally I just wrote some code that uses a binary search to find the highest possible values of HorizontalExtent and VerticalExtent that don’t throw an exception. It felt odd to do that, but of course the end users won’t care.

Some Possible Enhancements

  • If you’re using Silverlight 5, I imagine you could modify this class to talk to WIA using PInvoke instead of COM automation.
  • It might be nice to make the scanning asynchronous. That wasn’t required for the app I was writing, so I didn’t mess with it.

Source Code

using System;
using System.Runtime.InteropServices.Automation;
using System.Collections.Generic;

namespace WiaScanner
{
    ////////////////////////////////////////////////////////////////////////////
    /// <summary>
    /// Provides the ability to acquire images from a WIA scanner.
    /// </summary>
    ////////////////////////////////////////////////////////////////////////////
    public class WiaScanner
    {
        #region Public Methods

        ////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Prompts for a scanner to use (if there's more than one connected), and
        /// scans all pages available.
        /// </summary>
        /// <param name="onPageScanned">The action to take when a page is scanned.</param>
        ////////////////////////////////////////////////////////////////////////////
        public void ScanAllPages(Action<byte[]> onPageScanned, bool showUI = false)
        {
            if (showUI)
            {
                //
                // If we're showing the WIA common UI, then unfortunately we have to let WIA handle everything else too.
                // This means the feeder probably won't work correctly.
                //
                WiaImage image = WiaCommonDialog.ShowAcquireImageDialog();
                if (image != null)
                {
                    onPageScanned(image.Bytes);
                }
            }
            else
            {
                //
                // Get a connection to the scanner device (if there's more than one, prompt the user to choose).
                //
                WiaDevice wiaDevice = WiaDevice.Select();
                if (wiaDevice != null)
                {
                    if (wiaDevice.UsingFeeder)
                    {
                        //
                        // Scan the first page in the feeder.
                        //
                        WiaImage image = wiaDevice.GetNextPage(_defaultImageFormatId);
                        while (image != null)
                        {
                            //
                            // Let the caller do something with the page.
                            //
                            onPageScanned(image.Bytes);

                            //
                            // Scan the next page in the feeder.
                            //
                            image = wiaDevice.GetNextPage(_defaultImageFormatId);
                        }
                    }
                    else
                    {
                        //
                        // Scan a single page from the flatbed.
                        //
                        WiaImage image = wiaDevice.GetImageFromFlatbed(_defaultImageFormatId);
                        if (image != null)
                        {
                            onPageScanned(image.Bytes);
                        }
                    }
                }
            }
        }

        #endregion

        #region Private Classes

        #region Private Class WiaCommonDialog

        ////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Talks to the WIA.CommonDialog COM object, which provides some standard
        /// UI dialogs for selecting a scanner and acquiring an image.
        /// http://msdn.microsoft.com/en-us/library/ms630492(v=VS.85).aspx
        /// </summary>
        ////////////////////////////////////////////////////////////////////////////
        private static class WiaCommonDialog
        {
            #region Internal Methods

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Shows the acquire image dialog, which handles everything for us,
            /// including device selection, options such as quality/cropping, and
            /// doesn't allow us any programmatic access to the way it's done.
            /// </summary>
            /// <returns></returns>
            ////////////////////////////////////////////////////////////////////////////
            internal static WiaImage ShowAcquireImageDialog()
            {
                dynamic dialog = GetWiaCommonDialog();

                dynamic imageFile = dialog.ShowAcquireImage(
                    WiaDeviceType.Scanner,
                    WiaImageIntent.BlackAndWhite,
                    WiaImageBias.MinimizeSize,
                    _defaultImageFormatId,
                    false, // Don't show device selection unless there's more than one scanner to choose from.
                    false, // Only use the common UI if the driver doesn't provide its own.
                    false  // Don't throw an error if the user cancels.
                    );

                if (imageFile != null)
                {
                    //
                    // Wrap the dynamic image object inside a .NET object.
                    //
                    return new WiaImage(imageFile, _defaultImageFormatId);
                }
                else
                {
                    return null;
                }
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Shows the device selection dialog, but only if more than one
            /// scanner is available.
            /// </summary>
            /// <returns></returns>
            ////////////////////////////////////////////////////////////////////////////
            internal static dynamic ShowSelectDeviceDialog()
            {
                dynamic dialog = GetWiaCommonDialog();

                //
                // Prompt user to select device, if necessary.
                //
                return dialog.ShowSelectDevice(
                    WiaDeviceType.Scanner,
                    false, // Don't show select device dialog if only 1 device is available.
                    false  // Don't throw an error if the user cancels.
                    );
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Shows the Camera and Scanner Wizard, which is built into Windows and
            /// imports scanned images into a folder.
            /// </summary>
            /// <returns></returns>
            ////////////////////////////////////////////////////////////////////////////
            internal static void ShowWizard(WiaDevice device)
            {
                dynamic dialog = GetWiaCommonDialog();
                dynamic wiaDevice = device.Connect();

                //
                // Show wizard.
                //
                dialog.ShowAcquisitionWizard(wiaDevice);
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Shows the transfer dialog and returns the acquired image, or null if no
            /// image was acquired.
            /// </summary>
            /// <param name="nextItem">The item to transfer.</param>
            /// <param name="formatId">The <see cref="WiaFormatId"/> to acquire.</param>
            ////////////////////////////////////////////////////////////////////////////
            internal static WiaImage ShowTransferDialog(dynamic itemToTransfer, string formatId)
            {
                //
                // Scan the image and wrap it in a .NET object.
                //
                dynamic wiaImage = GetWiaCommonDialog().ShowTransfer(itemToTransfer, formatId, false);
                if (wiaImage != null)
                {
                    return new WiaImage(wiaImage, formatId);
                }
                else
                {
                    return null;
                }
            }

            #endregion

            #region Private Methods

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Initializes a new instance of the <see cref="WiaCommonDialog"/> class
            /// by getting/creating an instance of the WIA.CommonDialog COM object.
            /// </summary>
            /// <param name="wiaCommonDialog">The WIA COM object.</param>
            ////////////////////////////////////////////////////////////////////////////
            private static dynamic GetWiaCommonDialog()
            {
                return GetOrCreateAutomationObject(_wiaCommonDialogProgId);
            }

            #endregion

            #region Constants

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Specifies the type of device attached to a user's computer.
            /// http://msdn.microsoft.com/en-us/library/ms630792(v=VS.85).aspx
            /// </summary>
            ////////////////////////////////////////////////////////////////////////////
            private static class WiaDeviceType
            {
                internal const uint Unspecified = 0;
                internal const uint Scanner = 1;
                internal const uint Camera = 2;
                internal const uint Video = 3;
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Helps specify what type of data the image is intended to represent.
            /// http://msdn.microsoft.com/en-us/library/ms630796(v=VS.85).aspx
            /// </summary>
            ////////////////////////////////////////////////////////////////////////////
            private static class WiaImageBias
            {
                internal const uint MinimizeSize = 65536;
                internal const uint MaximizeQuality = 131072;
            }

            private const string _wiaCommonDialogProgId = "WIA.CommonDialog";

            #endregion
        }

        #endregion

        #region Private Class WiaDevice

        ////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Represents a WIA device and provides methods for interacting with it.
        /// </summary>
        ////////////////////////////////////////////////////////////////////////////
        private class WiaDevice
        {
            #region Internal Members

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Shows a dialog so the user can select an input device.
            /// </summary>
            /// <returns></returns>
            ////////////////////////////////////////////////////////////////////////////
            internal static WiaDevice Select()
            {
                dynamic wiaDevice = WiaCommonDialog.ShowSelectDeviceDialog();
                if (wiaDevice != null)
                {
                    //
                    // Wrap a .NET object around the dynamic device object.
                    //
                    return new WiaDevice(wiaDevice);
                }
                else
                {
                    return null;
                }
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Gets the image from the scanner's flatbed.
            /// </summary>
            /// <param name="formatId">The format id.</param>
            /// <returns></returns>
            ////////////////////////////////////////////////////////////////////////////
            internal WiaImage GetImageFromFlatbed(string formatId)
            {
                //
                // Connect to the actual device and get the image.
                //
                dynamic wiaDevice = this.ConnectToDevice();
                return this.GetImage(formatId, wiaDevice);
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Gets the next page.
            /// </summary>
            /// <param name="formatId">The format id.</param>
            /// <returns></returns>
            ////////////////////////////////////////////////////////////////////////////
            internal WiaImage GetNextPage(string formatId)
            {
                //
                // Connect to the actual device.
                // (For some reason, this needs to be done for each page or errors occur.)
                //
                dynamic wiaDevice = this.ConnectToDevice();

                //
                // Make sure there's another page (a.k.a. "item" in WIA terms) available.
                //
                if (this.HasMorePages(wiaDevice))
                {
                    return this.GetImage(formatId, wiaDevice);
                }

                return null;
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Connects to the device, and returns the dynamic WIA object used
            /// to talk to the device.
            /// </summary>
            /// <returns></returns>
            ////////////////////////////////////////////////////////////////////////////
            internal dynamic Connect()
            {
                return this.ConnectToDevice();
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Gets a value indicating whether this device connection is using a feeder,
            /// i.e. the device reports that it has a feeder, that it has
            /// the ability to detect a document loaded in the feeder, and that it
            /// actually was detecting a document loaded in the feeder when the scan
            /// was initiated.
            /// </summary>
            ////////////////////////////////////////////////////////////////////////////
            internal bool UsingFeeder { get; private set; }

            #endregion

            #region Private Methods

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Initializes a new instance of the <see cref="WiaDevice"/> class
            /// using an instance of the WIA.Device COM object.
            /// </summary>
            /// <param name="wiaDevice">The wia device.</param>
            ////////////////////////////////////////////////////////////////////////////
            private WiaDevice(dynamic wiaDevice)
            {
                if (wiaDevice != null)
                {
                    //
                    // Remember the device ID, because we'll have to reconnect for every page (for some unknown reason).
                    //
                    this._wiaDeviceId = wiaDevice.DeviceID;

                    //
                    // Find out if the device has a feeder containing a document.
                    //
                    uint capabilities = this.GetWiaItemPropertyValue<uint>(PropertyId.DocumentHandlingCapabilities, wiaDevice);
                    if (FlagsAreSet(capabilities,
                        DocumentHandlingCapability.HasFeeder |
                        DocumentHandlingCapability.CanDetectDocInFeeder))
                    {
                        this.UsingFeeder = this.HasDocumentInFeeder(wiaDevice);
                    }
                }
                else
                {
                    throw new ArgumentNullException("wiaDevice");
                }
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Connects to the device represented by <see cref="_wiaDeviceId"/>.
            /// </summary>
            /// <returns></returns>
            ////////////////////////////////////////////////////////////////////////////
            private dynamic ConnectToDevice()
            {
                dynamic deviceManager = GetOrCreateAutomationObject(_wiaDeviceManagerProgId);

                //
                // Find the device.
                //
                foreach (dynamic deviceInfo in deviceManager.DeviceInfos)
                {
                    if (deviceInfo.DeviceID == this._wiaDeviceId)
                    {
                        //
                        // Connect to it.
                        //
                        dynamic wiaDevice = deviceInfo.Connect();

#if DEBUG
                        this.OutputAllProperties(wiaDevice, "Just connected to device.");
#endif

                        //
                        // Set up its properties, since apparently they revert to defaults for each connection.
                        //
                        this.SetDeviceProperties(wiaDevice);

#if DEBUG
                        this.OutputAllProperties(wiaDevice, "Just set device properties.");
#endif

                        return wiaDevice;
                    }
                }

                throw new Exception("Unable to connect to device.");
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Sets the device's properties so that it uses the right paper source.
            /// </summary>
            /// <param name="wiaDevice">The WIA device to configure.</param>
            ////////////////////////////////////////////////////////////////////////////
            private void SetDeviceProperties(dynamic wiaDevice)
            {
                //
                // If there's an available feeder, tell the scanner to use it. Otherwise, use the flatbed.
                //
                if (this.UsingFeeder)
                {
                    this.SetWiaItemPropertyValue(PropertyId.DocumentHandlingSelect, DocumentHandlingSelect.Feeder, wiaDevice);
					this.SetWiaItemPropertyValue(PropertyId.ScanAheadPages, 0, wiaDevice);
					this.SetWiaItemPropertyValue(PropertyId.NumPagesToAcquireFromFeeder, 1, wiaDevice);
                }
                else
                {
                    this.SetWiaItemPropertyValue(PropertyId.DocumentHandlingSelect, DocumentHandlingSelect.Flatbed, wiaDevice);
                }
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Sets the image's properties, e.g. resolution, size, and color depth.
            /// </summary>
            /// <param name="wiaImageItem">The WIA image item.</param>
            ////////////////////////////////////////////////////////////////////////////
            private void SetImageProperties(dynamic wiaImageItem)
            {
                //
                // Set color depth to black and white.
                //
                this.SetWiaItemPropertyValue(PropertyId.CurrentIntent, WiaImageIntent.BlackAndWhite, wiaImageItem);

                //
                // Set resolution to default.
                //
                this.SetWiaItemPropertyValue(PropertyId.HorizontalResolution, _defaultResolution, wiaImageItem);
                this.SetWiaItemPropertyValue(PropertyId.VerticalResolution, _defaultResolution, wiaImageItem);

                if (!this.UsingFeeder)
                {
                    //
                    // Set size to maximum allowed by the flatbed.
                    // For some reason, some scanners don't scan the entire image by default,
                    // even if you set the paper size property. This seems like a bit of a hack,
                    // but it works.
                    //
                    this.SetWiaUintPropertyToMaximum(PropertyId.HorizontalExtent, wiaImageItem);
                    this.SetWiaUintPropertyToMaximum(PropertyId.VerticalExtent, wiaImageItem);
                }
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Sets the WIA property with the specified ID to the maximum value that doesn't
            /// throw an exception. Does this through trial and error for now, since
            /// there doesn't seem to be another way.
            /// </summary>
            /// <param name="propertyId">The property id.</param>
            /// <param name="wiaImageItem">The wia image item.</param>
            ////////////////////////////////////////////////////////////////////////////
            private void SetWiaUintPropertyToMaximum(uint propertyId, dynamic wiaImageItem)
            {
                uint lowValue = this.GetWiaItemPropertyValue<uint>(propertyId, wiaImageItem);
                uint highValue = lowValue * 2;

                //
                // Find a value that's too big, i.e. one that throws an exception.
                //
                bool tooBig = false;
                while (!tooBig)
                {
                    try
                    {
                        //
                        // Try setting the property to a higher value.
                        //
                        this.SetWiaItemPropertyValue(propertyId, highValue, wiaImageItem);

                        //
                        // If no exception was thrown, try an even higher value.
                        //
                        lowValue = highValue;
                        highValue *= 2;
                    }
                    catch
                    {
                        tooBig = true;
                    }
                }

                //
                // Now do a binary search to find the maximum.
                //
                this.RecursiveSetWiaUintPropertyToMaximum(lowValue, highValue, propertyId, wiaImageItem);
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Sets the WIA property with the specified ID to the maximum value (within
            /// the given range) that doesn't throw an exception. Does this by using a
            /// binary search to set different values until it finds the maximum.
            /// </summary>
            /// <param name="lowValue">The low value, which is know to not throw an exception.</param>
            /// <param name="highValue">The high value, which is known to throw an exception.</param>
            /// <param name="propertyId">The property id.</param>
            /// <param name="wiaImageItem">The wia image item.</param>
            ////////////////////////////////////////////////////////////////////////////
            private void RecursiveSetWiaUintPropertyToMaximum(uint lowValue, uint highValue, uint propertyId, dynamic wiaImageItem)
            {
                //
                // If highValue is 1 more than lowValue, then we can't go any higher, and
                // we should leave the property set the way it is. Otherwise, we need to try
                // another value.
                //
                if (lowValue < highValue - 1)
                {
                    //
                    // Try setting the property halfway between lowValue and highValue.
                    //
                    uint newValue = lowValue + (highValue - lowValue) / 2;
                    bool success = true;
                    try
                    {
                        this.SetWiaItemPropertyValue(propertyId, highValue, wiaImageItem);
                    }
                    catch
                    {
                        success = false;
                    }

                    if (success)
                    {
                        //
                        // Successful? Try a higher value.
                        //
                        this.RecursiveSetWiaUintPropertyToMaximum(newValue, highValue, propertyId, wiaImageItem);
                    }
                    else
                    {
                        //
                        // Unsuccessful? Try a lower value.
                        //
                        this.RecursiveSetWiaUintPropertyToMaximum(lowValue, newValue, propertyId, wiaImageItem);
                    }
                }
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Determines whether the device has more pages waiting to be acquired.
            /// This will return true if the device has a feeder and its status is reported
            /// as FEED_READY.
            /// </summary>
            /// <returns>
            /// 	<c>true</c> if the device has more pages; otherwise, <c>false</c>.
            /// </returns>
            ////////////////////////////////////////////////////////////////////////////
            private bool HasMorePages(dynamic wiaDevice)
            {
                //
                // If there's a feeder, check to see if it's "ready." If so, then we have more pages.
                //
                if (this.UsingFeeder)
                {
                    return this.HasDocumentInFeeder(wiaDevice);
                }
                else
                {
                    return false;
                }
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Determines whether the device has a document loaded in the feeder.
            /// </summary>
            /// <param name="wiaDevice">The wia device.</param>
            /// <returns>
            /// 	<c>true</c> if a document is in the feeder; otherwise, <c>false</c>.
            /// </returns>
            ////////////////////////////////////////////////////////////////////////////
            private bool HasDocumentInFeeder(dynamic wiaDevice)
            {
                uint status = this.GetWiaItemPropertyValue<uint>(PropertyId.DocumentHandlingStatus, wiaDevice);
                return FlagsAreSet(status, DocumentHandlingStatus.FeederReady);
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Gets the value of the property with the specified ID.
            /// </summary>
            /// <param name="propertyId">The ID of the desired property.</param>
            /// <param name="wiaItem">The WIA item whose property value is to be retrieved.</param>
            /// <returns></returns>
            ////////////////////////////////////////////////////////////////////////////
            private T GetWiaItemPropertyValue<T>(uint propertyId, dynamic wiaItem)
            {
                dynamic property = this.FindProperty(propertyId, wiaItem);
                if (property != null)
                {
                    return (T)property.Value;
                }
                else
                {
                    return default(T);
                }
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Sets the value of the property with the specified ID.
            /// </summary>
            /// <param name="propertyId">The property id.</param>
            /// <param name="newValue">The new value.</param>
            /// <param name="wiaItem">The item whose property is to be set.</param>
            ////////////////////////////////////////////////////////////////////////////
            private void SetWiaItemPropertyValue(uint propertyId, object newValue, dynamic wiaItem)
            {
                dynamic property = this.FindProperty(propertyId, wiaItem);
                if (property != null)
                {
                    property.Value = newValue;
                }
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Finds the property with the specified ID.
            /// </summary>
            /// <param name="propertyId">The property id.</param>
            /// <param name="wiaItem">The wia item.</param>
            /// <returns></returns>
            ////////////////////////////////////////////////////////////////////////////
            private dynamic FindProperty(uint propertyId, dynamic wiaItem)
            {
                //
                // If we've found this property before, return it.
                //
                if (this._propertyLookup.ContainsKey(propertyId))
                {
                    return wiaItem.Properties[this._propertyLookup[propertyId]];
                }
                else
                {
                    //
                    // Find the property with the specified ID.
                    //
                    uint i = 1; // (WIA uses 1-based arrays.)
                    foreach (dynamic property in wiaItem.Properties)
                    {
                        //
                        // Found it. Remember its index so we can find it quickly later,
                        // and return it.
                        //
                        if (property.PropertyID == propertyId)
                        {
                            this._propertyLookup.Add(propertyId, i);
                            return property;
                        }

                        i++;
                    }

                    //
                    // Didn't find it.
                    //
                    return null;
                }
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Transfers an image from the scanner to memory.
            /// </summary>
            /// <param name="formatId">The ID of the image format to use.</param>
            /// <param name="wiaDevice">The WIA device to talk to.</param>
            /// <returns></returns>
            ////////////////////////////////////////////////////////////////////////////
            private WiaImage GetImage(string formatId, dynamic wiaDevice)
            {
                //
                // For a scanner device, the first item represents the image to be scanned.
                //
                dynamic nextItem = wiaDevice.Items[1]; // WIA uses 1-based arrays.
                if (nextItem != null)
                {
#if DEBUG
                    this.OutputAllProperties(nextItem, "Here's the next image before setting properties.");
#endif

                    //
                    // There is an item available; set its properties and scan it.
                    //
                    this.SetImageProperties(nextItem);
#if DEBUG
                    this.OutputAllProperties(nextItem, "Here it is again after setting properties.");
#endif

                    dynamic nextImage = nextItem.Transfer(formatId);
                    if (nextImage != null)
                    {
                        //
                        // Wrap the dynamic scanned image object inside a .NET object.
                        //
                        return new WiaImage(nextImage, formatId);
                    }
                }

                return null;
            }

            #endregion

            #region Private Fields

            private string _wiaDeviceId;
            private Dictionary<uint, uint> _propertyLookup = new Dictionary<uint, uint>();
            private const string _wiaDeviceManagerProgId = "WIA.DeviceManager";

            #endregion

            #region Constants

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Defines WIA property IDs (according to the following MSDN page).
            /// http://msdn.microsoft.com/en-us/library/ms630202(VS.85).aspx
            /// </summary>
            ////////////////////////////////////////////////////////////////////////////
            private class PropertyId
            {
                // Device properties
                internal const uint DocumentHandlingCapabilities = 3086;
                internal const uint DocumentHandlingStatus = 3087;
                internal const uint DocumentHandlingSelect = 3088;
                internal const uint ScanAheadPages = 3094;
                internal const uint NumPagesToAcquireFromFeeder = 3096;

                // Item (a.k.a. Image) properties
                internal const uint BitsPerPixel = 4104;
                internal const uint CurrentIntent = 6146;
                internal const uint HorizontalResolution = 6147;
                internal const uint VerticalResolution = 6148;
                internal const uint HorizontalExtent = 6151;
                internal const uint VerticalExtent = 6152;
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Defines flags that describe a scanner's capabilities.
            /// (Possible values for WIA_DPS_DOCUMENT_HANDLING_CAPABILITIES)
            /// http://msdn.microsoft.com/en-us/library/ff551379(v=vs.85).aspx
            /// http://www.koders.com/c/fid685EF43A9C4BCB6120448A76824B136AD2C57A70.aspx?s=wmsserver.h
            /// </summary>
            ////////////////////////////////////////////////////////////////////////////
            [Flags]
            private enum DocumentHandlingCapability : uint
            {
                HasFeeder = 0x001,
                HasFlatbed = 0x002,
                HasDuplexer = 0x004,
                CanDetectDocOnFlatbed = 0x008,
                CanDetectDocOnlyByScanning = 0x010,
                CanDetectDocInFeeder = 0x020,
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Defines values that tell a WIA scanner whether to use its flatbed or ADF.
            /// </summary>
            ////////////////////////////////////////////////////////////////////////////
            private class DocumentHandlingSelect
            {
                internal const uint Feeder = 1;
                internal const uint Flatbed = 2;
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Defines values that indicate possible statuses of a WIA device.
            /// http://msdn.microsoft.com/en-us/library/ff551386(v=VS.85).aspx
            /// http://www.koders.com/c/fid685EF43A9C4BCB6120448A76824B136AD2C57A70.aspx?s=wmsserver.h
            /// </summary>
            ////////////////////////////////////////////////////////////////////////////
            [Flags]
            private enum DocumentHandlingStatus : uint
            {
                FeederReady = 0x01,
                FlatbedReady = 0x02,
                DuplexerReady = 0x04,
                FlatbedCoverUp = 0x08,
                PaperPathCovered = 0x10,
                FeederPaperJam = 0x20
            }

            private const uint _defaultResolution = 300;

            #endregion

#if DEBUG
            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Loops through the item's properties and outputs their values to the
            /// Debug window.
            /// </summary>
            /// <param name="wiaItem">The wia item.</param>
            /// <param name="message">The message.</param>
            ////////////////////////////////////////////////////////////////////////////
            private void OutputAllProperties(dynamic wiaItem, string message)
            {
                System.Diagnostics.Debug.WriteLine(String.Empty);
                System.Diagnostics.Debug.WriteLine(message);

                foreach (dynamic property in wiaItem.Properties)
                {
                    object name = property.Name;
                    object id = property.PropertyID;
                    object value = property.Value;
                    System.Diagnostics.Debug.WriteLine("{0} ({1}) = {2}", name, id, value);
                }
            }
#endif
        }

        #endregion

        #region Private Class WiaImage

        ////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Represents an image acquired from a WIA device.
        /// </summary>
        ////////////////////////////////////////////////////////////////////////////
        private class WiaImage
        {
            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Initializes a new instance of the <see cref="WiaImage"/> class, and converts
            /// the image to the specified format. (This can be needed when a WIA device
            /// doesn't natively support a particular image format.)
            /// </summary>
            /// <param name="wiaImage">The WIA image COM object to encapsulate.</param>
            /// <param name="formatId">The <see cref="WiaFormatId"/> to convert to,
            /// in case the WIA driver didn't already do that.</param>
            ////////////////////////////////////////////////////////////////////////////
            public WiaImage(dynamic wiaImage, string formatId)
            {
                if (wiaImage == null)
                {
                    throw new ArgumentNullException("wiaImage");
                }

                if (wiaImage.FormatID != formatId)
                {
                    //
                    // Convert to the given format. This code is adapted from the following page.
                    // http://msdn.microsoft.com/en-us/library/ms630819(v=vs.85).aspx#FilterSharedSample016
                    //
                    dynamic imageProcess = AutomationFactory.CreateObject(_wiaImageProcessProgId);
                    imageProcess.Filters.Add(imageProcess.FilterInfos["Convert"].FilterID);
                    dynamic pngFilter = imageProcess.Filters[1]; // This is a 1-based array.
                    pngFilter.Properties["FormatID"].Value = formatId;
                    this._wiaImage = imageProcess.Apply(wiaImage);
                }
                else
                {
                    this._wiaImage = wiaImage;
                }
            }

            ////////////////////////////////////////////////////////////////////////////
            /// <summary>
            /// Gets the raw bytes of the image.
            /// </summary>
            /// <value>The bytes.</value>
            ////////////////////////////////////////////////////////////////////////////
            public byte[] Bytes
            {
                get
                {
                    //
                    // Sources:
                    // http://msdn.microsoft.com/en-us/library/ms630564(v=VS.85).aspx
                    // http://msdn.microsoft.com/en-us/library/ms630518(v=VS.85).aspx
                    //
                    return this._wiaImage.FileData.BinaryData as byte[];
                }
            }

            #region Private Fields

            private dynamic _wiaImage;
            private const string _wiaImageProcessProgId = "WIA.ImageProcess";

            #endregion
        }

        #endregion

        #endregion

        #region Private Methods

        ////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Does a bitwise AND to see if the specified flags are set on <paramref name="value"/>.
        /// </summary>
        /// <param name="value">The value to test.</param>
        /// <param name="flags">The flags to test for.</param>
        /// <returns></returns>
        ////////////////////////////////////////////////////////////////////////////
        private static bool FlagsAreSet(uint value, Enum flags)
        {
            var intFlags = Convert.ToUInt32(Enum.ToObject(flags.GetType(), flags));
            return (value & intFlags) == intFlags;
        }

        ////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Gets an automation object with the specified prog ID, or creates one
        /// if there isn't one already available.
        /// </summary>
        /// <param name="progId">The prog id.</param>
        /// <returns></returns>
        ////////////////////////////////////////////////////////////////////////////
        private static dynamic GetOrCreateAutomationObject(string progId)
        {
            if (AutomationFactory.IsAvailable)
            {
                try
                {
                    return AutomationFactory.GetObject(progId);
                }
                catch
                {
                    return AutomationFactory.CreateObject(progId);
                }
            }
            else
            {
                throw new Exception("COM automation is not available.");
            }
        }

        #endregion

        #region Constants

        ////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Defines values that tell a WIA scanner whether to get the next item in
        /// color or grayscale or black & white.
        /// </summary>
        ////////////////////////////////////////////////////////////////////////////
        private static class WiaImageIntent
        {
            internal const uint Unspecified = 0;
            internal const uint Color = 1;
            internal const uint Grayscale = 2;
            internal const uint BlackAndWhite = 4;
        }

        ////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// WIA image format IDs, according to the following MSDN article.
        /// http://msdn.microsoft.com/en-us/library/ms630810(v=VS.85).aspx
        /// </summary>
        ////////////////////////////////////////////////////////////////////////////
        private static class WiaFormatId
        {
            public const string Bmp = "{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}";
            public const string Png = "{B96B3CAF-0728-11D3-9D7B-0000F81EF32E}";
            public const string Gif = "{B96B3CB0-0728-11D3-9D7B-0000F81EF32E}";
            public const string Jpeg = "{B96B3CAE-0728-11D3-9D7B-0000F81EF32E}";
            public const string Tiff = "{B96B3CB1-0728-11D3-9D7B-0000F81EF32E}";
        }

        private const string _defaultImageFormatId = WiaFormatId.Png;

        #endregion
    }
}
Advertisements

27 Comments

Leave a Comment
  1. Mahendra Rane / Mar 19 2012 1:47 AM

    hi..

    How to access above your application from one pc to another pc in silverlight5?

    Thanks & Regards,

    Mahendra

    • Andrew / Mar 19 2012 7:56 PM

      Hi Mahendra.

      Are you asking if one PC running the app can scan from a scanner that’s attached to another PC? If so, I’m not sure; you might search the web for WIA scanner sharing. There seem to be tools out there that let you share a scanner just as you can share a printer.

      If not, then would you please clarify your question?

      Thanks,
      Andrew

  2. hamed / Apr 22 2012 4:04 AM

    Hi
    my problem with your code is: it works correctly, but after performing scan action, the _pages variable just contains one item while all of my papers are scanned by the application. as my goal is to have a single .Tiff file which contains all scanned papers, I tried to change file format to Tiff, But I encountered some program bugs

    thanks a lot in advanced

  3. hamed / Apr 24 2012 11:47 PM

    thanks for your reply, I’m now trying to perform a scan which produces multiple .jpeg files and then convert them to a single .tiff image in server side.
    but, by just one time execution of this line of code :
    dynamic nextImage = nextItem.Transfer(formatId);
    all of pages are passed from the scanner. if this is correct why after that _pages contains just one item? and if no what should I do.
    by defining below const in PropertyId class:
    internal const uint pages_count = 3096;
    and then set it to 1 by :
    this.SetWiaItemPropertyValue(PropertyId.pages_count , 1, wiaDevice);

    I could to control the Transfer method to just request one page from feeder,
    and then I called Transfer k times for scanning k pages!

    but this is a time consuming action and instead of doing a single multi-page scanning action we are doing a single page scan multiple times!

    thanks a lot for any help

    • Andrew / Apr 25 2012 8:06 AM

      Hamed,

      Are you saying that this is happening?

      1. You load k pages into document feeder.
      2. Your app calls the Transfer method.
      3. All k pages are pulled into the feeder and scanned at once.
      4. Scanning completes.
      5. _pages.Count equals 1.

      If so, that’s actually different behavior than what I observed during my tests. The scanner I tested only scanned one page every time I called Transfer. That’s why my ScanAllPages method has a while loop in it. I wonder if you and I are seeing different results because we’re using different scanner devices. Maybe our scanners have different default values for property #3096, in which case I think your idea of setting it to 1 before transferring might work. Hopefully I’ll find some time to test this again and find out, but could you please let me know if that works for you?

      Now I’m thinking back to when you scanned your pages in TIFF format and you were only getting one item in the _pages list. I wonder if your scanner was already creating a single multi-page TIFF for you. You said you encountered some program bugs when you tried TIFF though; can you be more specific?

      Also, can you please tell me what happens if you save your single byte array to a file and then try to open that file?

      Thanks for your comments!

  4. hamed / Apr 30 2012 9:12 AM

    yes, _pages.Count equals 1, it happens when I use kodak i1210 plus scanner.

    and for your second question I shuld say (again) yes, that works for me, by setting property #3096 to 1, in each call of transfer method one page is reading from feeder and finally _pages contains all pages that I put in feeder. every thing is correct, but as I sead previously, the problem of this solution is: instead of scanning k pages at a time, I’m calling scan action in mode of one page for k times! this is so much time consuming.

    I get the below error when I set: _defaultImageFormatId = WiaFormatId.Tiff;

    Error HRESULT E_FAIL has been returned from a call to a COM component.

    BUT: as I test, using Fujitsu 5220C scanner, WiaScanner.cs can just scan one page and this is by set ShowUI variable to true. when I set ites default source on feeder(instead of flatbed) and set ShowUI to false, it is not working correctly( I need more time to check it, I think it -maybe- is because of hardware problems )

    in any way, I should support scan action in silverlight by at least 2 different kind of scanners,
    if you have an idea, let me know & Thanks for your comments, too!

    • Andrew / Apr 30 2012 12:25 PM

      Thanks for replying. I incorrectly thought you hadn’t yet tried setting property 3096 to 1; sorry about that.

      With the scanner I tested, it wasn’t time consuming to scan one page per Transfer call. I guess the Kodak i1210 is pausing in between pages when you transfer one page at a time? Maybe there are other properties we need to experiment with.

      When you don’t set property 3096 to 1, I’m curious if the i1210 is smashing all the pages into a single image for you. (Have you tried opening the image it gives you?) I wonder what would happen if you set property 3094 (WIA_DPS_SCAN_AHEAD_PAGES) to 1 instead of property 3096. http://msdn.microsoft.com/en-us/library/ff551423(v=vs.85).aspx

      I also wonder what would happen if you played with different values for WIA_DPS_DOCUMENT_HANDLING_SELECT. http://msdn.microsoft.com/en-us/library/ff551384(v=vs.85).aspx This seems like a longshot that would only have any effect in WinXP, but maybe you could add “internal const uint NextPage = 0x080;” to the DocumentHandlingSelect class after line 798, and pass “DocumentHandlingSelect.Feeder & DocumentHandlingSelect.NextPage” in line 437.

      Regarding the HRESULT E_FAIL error, which specific call is getting that error?

      I’m interested to know what else you learn with the Fujitsu 5220C too.

      Good luck!

  5. hamed / May 1 2012 8:34 AM

    Hello againg!
    setting 3094 and sending DocumentHandlingSelect.Feeder & DocumentHandlingSelect.NextPage” in line 437 have not any usable effect on any of my two scanners,
    HRESULT E_FAIL error is going to raise by calling .Transfer method.

    now I want your idea about this changes ( I tested them – also I changed fujitsu 5220 to fujitsu 6230 because of hardware problems)

    the goal is to get an array of byte vectors:
    in i1210 by setting property 3096 to 1 and calling nextItem.Transfer(formatId) k times for scanning k pages every thing is correct.
    in 6230 I replaced (A)
    uint capabilities = this.GetWiaItemPropertyValue(PropertyId.DocumentHandlingCapabilities, wiaDevice);
    if (FlagsAreSet(capabilities,
    DocumentHandlingCapability.HasFeeder |
    DocumentHandlingCapability.CanDetectDocInFeeder))
    {
    this.UsingFeeder = this.HasDocumentInFeeder(wiaDevice);
    }
    whith this: (B)
    this.UsingFeeder = true;

    to get my own result and every thing is correct,

    summmary:
    to have a situation in which both i1210 and 6230 work, we must have (intersection)
    1- (A) => (B) replacement, ( to work correct in 6230)( but can this make some future problem????! I don’t know anythind)
    2- have a control such that to scan k pages, call .Transfer just k times like this (just before Transfer method)

    if( Page_counter>=k)
    return null;

    3- in i1210 we should set 3096 to 1, but in 6230 such setting makes no change

    I’m waiting for your reply,
    thanks for all of your helps and guides!

  6. Andrew / May 1 2012 4:13 PM

    Hi Hamed,

    Sorry to hear that prop 3094 had no effect.

    I’m not sure what to say about the Transfer method throwing that E_FAIL. I’d be surprised if the scanner’s WIA driver doesn’t support TIFF. Since we are both content to use another format on the client side, I guess we can let that mystery go unsolved.

    1- I hope we can find another way to make the 6230 work. I am guessing that (B) could cause problems in two scenarios: (i) using a flatbed-only scanner, and (ii) using a scanner with both a flatbed and a feeder where the feeder is empty, and the user expects to scan from the flatbed. Do you know what exactly is causing UsingFeeder to be set to false? For example, perhaps the 6230 is reporting true for HasFeeder but false for CanDetectDocInFeeder? Or, perhaps HasDocumentInFeeder is returning false because the 6230 is not reporting FeederReady?

    2- I don’t understand why this additional code is necessary. Is the while loop (line 51) not working? Or maybe HasMorePages is not working?

    3- If the 6230 behaves the same either way, then I propose we add a line of code after line 437 that always sets property 3096 to 1. Is that not causing a performance problem for you anymore?

    Thanks,
    Andrew

    • Andrew / May 3 2012 2:24 PM

      Oops, I just realized that I meant to suggest property 3094 be set to 0, not 1. MSDN says, “If the WIA_DPS_SCAN_AHEAD_PAGES property is zero, scan ahead is disabled, and the scanner will not scan ahead any pages.” http://msdn.microsoft.com/en-us/library/ff551423(v=vs.85).aspx Hamed, does that change anything for you?

      I tried it with my scanner (HP Officejet 4500), and it doesn’t seem to have any effect; I don’t even see prop 3094 listed in the results of the OutputAllProperties call from the ConnectToDevice method. Presumably, the HP OfficeJet 4500 doesn’t support this optional property.

      I checked the HP’s default value for property 3096: it is indeed already set to 1 before SetDeviceProperties is called.

      I also tried hard-coding UsingFeeder to true just to see what would happen. If there’s no page in the feeder, the HP does nothing. If the feeder detection doesn’t work for you, then we might have to enhance WiaScanner to allow you to pass it a variable to indicate where the user wants to scan from.

  7. hamed / May 6 2012 12:16 AM

    Hi again andrew!
    your guess is true: the 6230 is reporting true for HasFeeder but false for CanDetectDocInFeeder.
    also HasMorePages is not working correctly in 6230.
    also setting 3096 to 1 for always is correct and makes no performance problem.
    we replaced (B) with:
    uint capabilities = this.GetWiaItemPropertyValue(PropertyId.DocumentHandlingCapabilities, wiaDevice);
    if (FlagsAreSet(capabilities,
    DocumentHandlingCapability.HasFeeder )
    {
    this.UsingFeeder = true;
    }
    to alleviate the two problems you mentioned.

    because both HasDocumentInFeeder and CanDetectDocInFeeder are not working correctly. we also surround the Transfer method with a try-catch block to alleviate lack of this two problem. by using try-catch block, it is not necessary to use such an extra code like:

    if( Page_counter>=k)
    return null;

    for converting list of byte array to a single tiff image we used LibTiff.Net library which introduced in

    http://stackoverflow.com/questions/10295854/how-to-convert-a-list-of-byte-arrays-to-a-single-multi-page-tiff-image-in-silver#comment13252819_10295854

    now we proceed temporaly by these changes, and in near future we try to make it better.

    how can I appreciate your good helps?

  8. Joel Frojmowicz / Jul 2 2012 11:51 AM

    Hi guys,

    Do anyone solved that problem of scanning 1 multipage document instead of scanning several pages of a document?

    I still have this issue on a HP Scanjet N6010. If I use the software boundled with the hardware, everything works as desired. But If I try to use WIA to do the scanning I get the undersireable solution…

    Please Help!!!
    Joel Frojmowicz.

    • Andrew / Jul 2 2012 4:12 PM

      Hi Joel,

      I just updated the source code, specifically in the SetDeviceProperties method and the PropertyId class. These changes are based on my conversation with Hamed (see comments above). My apologies for not updating sooner. Please let me know if that fixes your problem or not.

      If it doesn’t, I’d recommend looking in Visual Studio’s Output window at run time, to see what your scanner’s property values are. As you can see, the SetDeviceProperties method writes all of the device’s properties to this window, both before and after WiaScanner makes changes to them. That might provide a clue as to what’s wrong.

      Hope that helps!
      Andrew

  9. chandra / Jul 11 2012 2:46 AM

    Hi,
    how to download the sample application of this

    • Andrew / Aug 9 2012 10:51 AM

      Hi Chandra. Sorry, but I don’t have a sample app available, because I developed this as part of a large system at work and then posted the WiaScanner code with my employer’s permission. Of course as you can see, the first code snippet at the top of this post demonstrates how to consume WiaScanner; you just need to modify line 7 to do something different with the byte arrays that come back. What are you trying to do with the scanned images?

  10. Mahendra Rane / Jul 16 2012 12:57 AM

    Hi,

    This code is supported to my HP Scan Feeder but not supported to Canon Scan Feeder. Do u have any idea how to solve this issue.

    Thanks & regards,

    Mahendra Rane

    • Andrew / Aug 9 2012 10:53 AM

      Hi Mahendra. Can you please provide more details about the problem you’re experiencing?

  11. jpressley / Aug 7 2012 10:30 AM

    Hello,
    Got is running beautifully with Silverlight 5 on local machine, but when I publish, it will not run on client machine. Does the project need to be set to enable running application out of browser or can just run if require elevated trust when running in-brower?

    • Andrew / Aug 9 2012 11:06 AM

      Hi jpressley. That’s a good question. When I did this in Silverlight 4, in-browser elevated trust was not an option, so unfortunately I don’t have any experience with that kind of deployment. I just did a quick web search and found this article on MSDN though: http://msdn.microsoft.com/en-us/library/gg192793(v=vs.95). Have you already tried that procedure on the client machine?

  12. Hamed / Aug 26 2012 7:44 AM

    Hi Andrew!

    thanks a lot for all of your previous replies to me about this article!
    Now, our project is working perfectly with scanners with WIA driver, (except of low speed!, but not much important for current situation)
    as I now WIA and TWAIN are of most public drivers which are used by many of scanners.
    for now, the problem is scanners with TWAIN driver, which is not supported by your class,
    Is there any solutions?

    thanks for any reply in advanced!

    • Andrew / Aug 27 2012 10:06 PM

      Hi Hamed, welcome back! Unfortunately I’m not aware of a simple answer to your question, but I’ll try to concisely communicate what I learned when I did this last year.

      As you probably know, WIA is a COM component that is pre-installed on Windows machines. In order to work with TWAIN, you need some other COM component to talk to. There are some non-free solutions for this, from LEADTOOLS and Accusoft.

      Another way to approach this is to take a non-Silverlight .NET component and write a COM wrapper for it. Both companies mentioned above offer non-SL components. I did a very small test using another (ImageMan.NET) since I already had access to it. I only took my COM wrapper as far as being able to show a list of scanners.

      Whether you write a COM wrapper or buy a third party COM component, you have to somehow register the COM component on every client machine. If you went with a third party, then presumably their installer would do this; otherwise you’d have to write your own installer or programmatically invoke regsvr32.exe from the Silverlight app. The company I work for decided not to pursue TWAIN at the time, partly because of this extra stuff.

      As you can see, this isn’t the simplest thing to jump into, but there’s plenty more to read on MSDN if you’re willing.

      Hopefully some of that was helpful to you!

  13. Paul Yung / Feb 11 2013 12:14 PM

    How can I handle double sided scan ?

  14. Anand / Jan 7 2014 10:58 PM

    Am developed one project in silverlight for scanning the document ……. locally its work fine…. then i put site to the IIS server…. the site Name is : testscanner…… then run from the IIS server…. the url is: “http://localhost/testscanner/” the project is work fine and able to scan the documents… like when i entered IIS Server ip address in url for example “http://10.164.2.112/testscanner/” means i got a error “COM automation is not available.”

  15. Anand / Jan 8 2014 12:58 AM

    I have developed a application as on the below link. https://andrewolson.wordpress.com/2011/10/19/adf-scanning-with-silverlight/.
    Deployed in IIS 7.0 in windows7 64 bit system developed using Silverlight 5.0 and Visual studio 2010.
    It works fine in local system. When I configured in server(windows 2008 64 bit, IIS 7.0) and accessed the application in client system it’s not working, Throwing exception as “COM Automation is not available”. Debugging the application in client system, the following line of code is not returning any value:
    dynamic dialog = GetWiaCommonDialog();
    When I call the application in client system with the following format is also not working:
    http:\\hostname(or)IP\application name in IIS.
    But it works fine in local host
    http:\\localhost\application name in IIS.
    Help me to solve this issue.

  16. Mittal / May 5 2014 4:41 AM

    Hi Andrew..

    This code is working fine for me..but i am not getting the property for two side scan. can you please help… how can i get two images (front & back) of a document on “nextItem.Transfer(formatId);” ?

  17. Shrikant Jadhav / Sep 5 2015 12:16 AM

    Hi Andrew,

    I am implementing an example by :

    http://10rem.net/blog/2010/04/14/scanning-an-image-from-silverlight-4-using-wia-automation
    Thanks so much for the great article. The code works great. Is this possible to create User Interface for Document Scanning. Here is the list of functionality I want to build in my web application using silver-light application:
    1. Preview Image
    2. In which image format want to save
    3. Set height and width to image
    4. After that scan that image
    5. After scan image will save on web server

    Can please help me for above functionality to implement in silver-light application.

    Thanks.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s