Scanning from the ADF using WIA in C#

 

I've been going nuts trying to scan from the document feeder on my Canon imageClass MF4150. Everything worked as expected from the flatbed, no dice trying to persuade the ADF to kick in. I found some sample code but it was oriented towards devices that can detect when a document is available in the feeder. Evidently my Canon doesn't expose this and so needs to be told the source to use.

The way to do this is to set the WIA_DPS_DOCUMENT_HANDLING_SELECT property to FEEDER. You then read WIA_DPS_DOCUMENT_HANDLING_STATUS to check that it's in the right mode and initiate the scan. This did not work for toffee.

After much experimentation I discovered a solution. I had been setting device properties and then setting item properties before requesting the scan. Switching the order - item then device - made everything work.

Here's the function to scan one page:

private XImage ScanOne()
{
    XImage ximage = null;

    try
    {
        // find our device (scanner previously selected with commonDialog.ShowSelectDevice)
        DeviceManager manager = new DeviceManager();
        DeviceInfo deviceInfo = null;
        foreach (DeviceInfo info in manager.DeviceInfos)
        {
            if (info.DeviceID == _deviceId)
            {
                deviceInfo = info;
            }
        }

        if (deviceInfo != null)
        {                    
            Device device = deviceInfo.Connect();
            CommonDialog commonDialog = new CommonDialog();

            Item item = device.Items[1];
            int dpi = 150;

            // configure item
            SetItemIntProperty(ref item, 6146, 2); // greyscale
            SetItemIntProperty(ref item, 6147, dpi); // 150 dpi
            SetItemIntProperty(ref item, 6148, dpi); // 150 dpi
            SetItemIntProperty(ref item, 6151, (int)(dpi * _width)); // scan width
            SetItemIntProperty(ref item, 6152, (int)(dpi * _height)); // scan height
            SetItemIntProperty(ref item, 4104, 8); // bit depth

            int deviceHandling = _adf ? 1 : 2; // 1 for ADF, 2 for flatbed

            // configure device
            SetDeviceIntProperty(ref device, 3088, deviceHandling); 
            int handlingStatus = GetDeviceIntProperty(ref device, 3087);

            if (handlingStatus == deviceHandling)
            {
                ImageFile image = commonDialog.ShowTransfer(item, formatJpeg, true);

                // save image to a temp file and then load into an XImage
                string tempPath = System.IO.Path.GetTempFileName();
                File.Delete(tempPath);
                tempPath = System.IO.Path.ChangeExtension(tempPath, "jpg");
                image.SaveFile(tempPath);
                ximage = XImage.FromFile(tempPath);

                _tempFilesToDelete.Add(tempPath);
            }
        }
    }
    catch (COMException ex)
    {
        ximage = null;

        // paper empty
        if ((uint)ex.ErrorCode != 0x80210003)
        {
            throw;
        }
    }

    return ximage;
}

A few notes — XImage is a type from PDFSharp. I wrote this as part of a PDF scanner that I'll post next so the scanned images are saved and then loaded into an XImage for rendering to the PDF document. The magic numbers come from WiaDef.h in the Platform SDK. If the ADF is out of pages this method sets the return image to null and eats the exception. This is because the function is called repeatedly to scan in pages until the ADF is empty if _adf is true (otherwise it grabs one image from the flatbed). 

If you've been banging your head against a wall trying to get WIA to work with a document feeder I hope this helps.

Tags: , ,

Categories: .NET | C#

Comments

Hugh
Can you post the full code?  I can't resolve SetItemIntProperty or SetDeviceIntProperty.

Thanks,
Hugh
# by Hugh United States on Thursday, August 5 2010 at 11:59 | Reply
Rob
Here are the methods to drop in:

private void SetDeviceIntProperty(ref Device device, int propertyID, int propertyValue)
{
    foreach (Property p in device.Properties)
    {
        if (p.PropertyID == propertyID)
        {
            object value = propertyValue;
            p.set_Value(ref value);
            break;
        }
    }
}

private int GetDeviceIntProperty(ref Device device, int propertyID)
{
    int ret = -1;

    foreach (Property p in device.Properties)
    {
        if (p.PropertyID == propertyID)
        {
            ret = (int)p.get_Value();
            break;
        }
    }

    return ret;
}

private void SetItemIntProperty(ref Item item, int propertyID, int propertyValue)
{
    foreach (Property p in item.Properties)
    {
        if (p.PropertyID == propertyID)
        {
            object value = propertyValue;
            p.set_Value(ref value);
            break;
        }
    }
}

private int GetItemIntProperty(ref Item item, int propertyID)
{
    int ret = -1;

    foreach (Property p in item.Properties)
    {
        if (p.PropertyID == propertyID)
        {
            ret = (int)p.get_Value();
            break;
        }
    }

    return ret;
}
# by Rob United States on Thursday, August 5 2010 at 14:00 | Reply
Hugh
(grin) Thanks Rob.  I've been reading a bunch about WIA the last couple days and I just went ahead and implemented those yesterday afternoon as Extension methods on the Properties object and also another helpful one on the COMException object.  Check 'em out:


using System.Runtime.InteropServices;
using WIA;
using System.Drawing.Imaging;

namespace WIA2test
{
    public static class WIAExtender
    {
        public static string TiffFormat = ImageFormat.Tiff.Guid.ToString("B");
    
        // copies of constants that are found in WiaDef.h

        public const int WIAFacility = 33;

        public const int WIA_ERROR_GENERAL_ERROR = 1;
        public const int WIA_ERROR_PAPER_JAM = 2;
        public const int WIA_ERROR_PAPER_EMPTY = 3;
        public const int WIA_ERROR_PAPER_PROBLEM = 4;
        public const int WIA_ERROR_OFFLINE = 5;
        public const int WIA_ERROR_BUSY = 6;
        public const int WIA_ERROR_WARMING_UP = 7;
        public const int WIA_ERROR_USER_INTERVENTION = 8;
        public const int WIA_ERROR_ITEM_DELETED = 9;
        public const int WIA_ERROR_DEVICE_COMMUNICATION = 10;
        public const int WIA_ERROR_INVALID_COMMAND = 11;
        public const int WIA_ERROR_INCORRECT_HARDWARE_SETTING = 12;
        public const int WIA_ERROR_DEVICE_LOCKED = 13;
        public const int WIA_ERROR_EXCEPTION_IN_DRIVER = 14;
        public const int WIA_ERROR_INVALID_DRIVER_RESPONSE = 15;
        public const int WIA_S_NO_DEVICE_AVAILABLE = 21;


        public const int WIA_COMPRESSION_JPEG = 5;        

        public const int WIA_PROPERTY_CurrentIntent = 6146;
        public const int WIA_PROPERTY_HorizontalResolution = 6147;
        public const int WIA_PROPERTY_VerticalResolution = 6148;
        public const int WIA_PROPERTY_HorizontalExtent = 6151;
        public const int WIA_PROPERTY_VerticalExtent = 6152;
        public const int WIA_PROPERTY_DocumentHandlingSelect = 3088;
        public const int WIA_PROPERTY_DocumentHandlingStatus = 3087;

        public const int WIA_PROPERTY_BitsPerPixel = 4104;
        public const int WIA_PROPERTY_Format = 4106;
        public const int WIA_PROPERTY_Compression = 4107;

        public const int WIA_PROPERTY_VALUE_Color = 1;
        public const int WIA_PROPERTY_VALUE_Gray = 2;
        public const int WIA_PROPERTY_VALUE_BlackAndWhite = 4;
        

        public static string GetErrorCodeDescription(int errorCode)
        {
            string desc = null;

            switch (errorCode)
            {
                case (WIA_ERROR_GENERAL_ERROR):
                    {
                        desc = "A general error occurred";
                        break;
                    }
                case (WIA_ERROR_PAPER_JAM):
                    {
                        desc = "There is a paper jam";
                        break;
                    }
                case (WIA_ERROR_PAPER_EMPTY):
                    {
                        desc = "The feeder tray is empty";
                        break;
                    }
                case (WIA_ERROR_PAPER_PROBLEM):
                    {
                        desc = "There is a problem with the paper";
                        break;
                    }
                case (WIA_ERROR_OFFLINE):
                    {
                        desc = "The scanner is offline";
                        break;
                    }
                case (WIA_ERROR_BUSY):
                    {
                        desc = "The scanner is busy";
                        break;
                    }
                case (WIA_ERROR_WARMING_UP):
                    {
                        desc = "The scanner is warming up";
                        break;
                    }
                case (WIA_ERROR_USER_INTERVENTION):
                    {
                        desc = "The scanner requires user intervention";
                        break;
                    }
                case (WIA_ERROR_ITEM_DELETED):
                    {
                        desc = "An unknown error occurred";
                        break;
                    }
                case (WIA_ERROR_DEVICE_COMMUNICATION):
                    {
                        desc = "An error occurred attempting to communicate with the scanner";
                        break;
                    }
                case (WIA_ERROR_INVALID_COMMAND):
                    {
                        desc = "The scanner does not understand this command";
                        break;
                    }
                case (WIA_ERROR_INCORRECT_HARDWARE_SETTING):
                    {
                        desc = "The scanner has an incorrect hardware setting";
                        break;
                    }
                case (WIA_ERROR_DEVICE_LOCKED):
                    {
                        desc = "The scanner device is in use by another application";
                        break;
                    }
                case (WIA_ERROR_EXCEPTION_IN_DRIVER):
                    {
                        desc = "The scanner driver reported an error";
                        break;
                    }
                case (WIA_ERROR_INVALID_DRIVER_RESPONSE):
                    {
                        desc = "The scanner driver gave an invalid response";
                        break;
                    }
                default:
                    {
                        desc = "An unknown error occurred";
                        break;
                    }
            }

            return desc;
        }

        public static int GetWIAErrorCode(this COMException cx)
        {
            int origErrorMsg = cx.ErrorCode;

            int errorCode = origErrorMsg & 0xFFFF;

            int errorFacility = ((origErrorMsg) >> 16) & 0x1fff;

            if (errorFacility == WIAFacility)
                return errorCode;

            return -1;
        }

        public static void SetProperty(this WIA.Properties searchBag, int propID, object propValue)
        {
            foreach (Property prop in searchBag)
            {
                if (prop.PropertyID == propID)
                {
                    prop.set_Value(ref propValue);
                    return;
                }
            }
        }

        public static object GetProperty(this WIA.Properties searchBag, int propID)
        {
            foreach (Property prop in searchBag)
            {
                if (prop.PropertyID == propID)
                {
                    return prop.get_Value();
                }
            }
            return null;
        }
    }
}



Then the usage looks like:


item.Properties.SetProperty(WIAExtender.WIA_PROPERTY_CurrentIntent, WIAExtender.WIA_PROPERTY_VALUE_Gray);


int handlingStatus = (int)scannerDevice.Properties.GetProperty(WIAExtender.WIA_PROPERTY_DocumentHandlingStatus);


try
{
  // some WIA operation
}
                catch (COMException cx)
                {
                    int comErrorCode = cx.GetWIAErrorCode();
                    if (comErrorCode > 0)
                    {
                        MessageBox.Show(WIAExtender.GetErrorCodeDescription(comErrorCode));
                    }
                }
# by Hugh United States on Friday, August 6 2010 at 09:02 | Reply
Rob
Hugh - very nice! Thanks for sharing that.
# by Rob United States on Friday, August 6 2010 at 09:50 | Reply
Aurelio
How do you determine the value of the "_adf" variable? I need to detect if the scanner has a flatbed, but I can't figure out how to do it.

Thanks,

Aurelio
# by Aurelio Portugal on Wednesday, August 25 2010 at 04:17 | Reply
Rob
Aurelio, _adf in the above snippet is set by the use - true to use the feeder, false to use the flat bed. It then sets the appropriate mode.

To see if the scanner has a flat bed check the WIA_DPS_DOCUMENT_HANDLING_CAPABILITIES property (msdn.microsoft.com/.../ff551379(v=VS.85).aspx).
# by Rob United States on Wednesday, August 25 2010 at 08:36 | Reply
Rob
Sorry bad link, try: msdn.microsoft.com/.../ff551379(v=VS.85).aspx
# by Rob United States on Wednesday, August 25 2010 at 08:37 | Reply
Aurelio
Thanks a lot Rob!
# by Aurelio Portugal on Thursday, August 26 2010 at 01:37 | Reply

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading



 

CC | About | Search | Home | Validate