Home | Random | Downloads | About | Hikes | 7,197,471,472 | Search | Feed

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.

Share

Comments

Hugh



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

Thanks,
Hugh

August 05, 2010

Rob Ellison



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;
}

August 05, 2010

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));
}
}

August 06, 2010

Rob Ellison



Hugh - very nice! Thanks for sharing that.

August 06, 2010

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

August 25, 2010

Rob Ellison



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 (http://msdn.microsoft.com/en-u....

August 25, 2010

Rob Ellison



Sorry bad link, try: http://msdn.microsoft.com/en-u...

August 25, 2010

Aurelio



Thanks a lot Rob!

August 25, 2010

Stephen



This code doesn't work for me. It stalls after checking the handling status. It acts like it is not setting the device handling to the feeder. Is there an issue with specific devices not being able to be set from WIA?

September 27, 2010

Rob Ellison



No exception? It just hangs?

I know that some devices can detect if a document is in the ADF in which case you might not need to set the ADF option before scanning. If you comment this out does it get any further?

September 27, 2010

Stephen



It throws an exception that the value is not in range when the ShowTransfer method is called. If I comment out trying to change the prop, it automatically selects the flatbed.

September 27, 2010

Stephen



I took out the check for status and it throws a value out of expected range exception when it calls ShowTransfer. If I don't try to set the prop, it automatically goes to the flatbed. This is driving me insane.

September 27, 2010

Rob Ellison



What is the device that you're trying to work with? Have you tried checking to see if you have the latest drivers installed?

September 27, 2010

Stephen



Brother MFC-7340. I uninstalled and reinstsalled the device, double checked that the drivers were accurately installed and I get the same results.

September 27, 2010

Rob Ellison



What is reported for the WIA_DPS_DOCUMENT_HANDLING_CAPABILITIES property (see comments above with MSDN link)?

September 27, 2010

Stephen



It comes back as 3. I'm not certain which constant that is. I am on Win 7 64bit.

September 27, 2010

Rob Ellison



Sorry - need to fix these nested comments, getting a bit narrow down here.

'3' is FEED and FLAT, so no auto-detection capability. You can get the constant values from WiaDef.h (in the Platform SDK).

I'm out of ideas for now. I'll post back if I think of anything else. Please let me know if you figure this out so I can update the sample code if necessary.

September 27, 2010

Jonaze



I have exactly the same problem with a Brother 6490, property 3087 always return "3". I have found the following explanation on msdn : http://msdn.microsoft.com/en-u....

But I don't found any solution to use the feeder. Normally it is the device which must select the ADF automatically.

October 26, 2010

Jonaze



I found a solution, set the property "3096" (Pages) to 1

October 31, 2010

David Wetherell



Great post ... I looked all over the place for a decent WIA example example and was about to give up when I uncovered this ! I was amazed that everything was pointing to a 10 yr article on codeproject for some crap twain example. I'm gonna post this link to some of the threads on stack overflow, hopefully will save some people some time ... Thank you, much ...

March 23, 2011

romrom



Looks good, but when I ran it with an HP OfficeJet Pro 8500A Plus, the ADF pulled two pages (instead of one), but the code only spit out a single JPG.   

May 18, 2011

Rob Ellison



Did you get a device progress dialog, and did this appear once or twice? Are the DPI/Width/Height set correctly for the paper type that you're scanning? Not sure what else to suggest other than maybe checking to see if updated firmware or drivers exist for your device (could be a device bug).

May 18, 2011

ameerasajid



My application works well with HP printers (HP Scanjet 7650 and HP officejet Pro L7780) but it doesnt work with brothersoft 9460cdn. Any idea what issue it could be?

August 23, 2012

Rob Ellison



Do you know where it's failing? Any exception or error code? Hard to troubleshoot without a specific issue. There are a few earlier posts about a Brother device in the thread, could it be the same issue?

August 24, 2012

I have a similar problem: need a method to get the multiples images obtained from a EPSON GT-S80 scanner. Always throws me the error 0x80210003. you could guide me to fix my method? (Juan Francisco Binaghi)

I Thought He Came With You
Robert Ellison's blog.

Like I Thought He Came With You on Facebook