Scanning with WIA automatic feeder scanner fails for second page
Asked Answered
G

2

7

I'm trying to scan multiple pages using a scanner which has an automatic feeder. My code is very simple at the moment:

WIA.CommonDialog dialog = new WIA.CommonDialog();
WIA.Device device = dialog.ShowSelectDevice(WIA.WiaDeviceType.ScannerDeviceType);
WIA.Items items = dialog.ShowSelectItems(device);
foreach (WIA.Item item in items)
{
    while (true)
    {
        try
        {
            WIA.ImageFile image = (WIA.ImageFile)dialog.ShowTransfer(item);
            if (image != null && image.FileData != null)
            {
                dynamic binaryData = image.FileData.get_BinaryData();
                if (binaryData is byte[])
                    using (MemoryStream stream = new MemoryStream(binaryData))
                    using (Bitmap bitmap = (Bitmap)Bitmap.FromStream(stream))
                    {
                        bitmap.Save(@"C:\Temp\scan.jpg", ImageFormat.Jpeg);
                    }
            }
        }
        catch (COMException)
        {
            break;
        }
    }
}

I tried querying the WIA_DPS_DOCUMENT_HANDLING_STATUS property to see if there are any pages available in the feeder, but that always returns 1, so instead I'm catching a COM exception an if I get the WIA_ERROR_PAPER_EMPTY, I know that the document feeder is empty.

The trouble is that this code only scans the first page, when the dialog.ShowTransfer method is called again, I get an exception with and E_FAIL HResult and I can't scan any more pages. Strangely enough, when I step through this code in the debugger, everything works fine and all pages are scanned.

I tried freeing up the image object by doing Marshal.ReleaseComObject(image) and image = null, but that didn't help. Neither did the suggestions from this question. Is there anything I'm doing wrong that's causing these errors?

EDIT: I wasn't able to find a good solution for this. The scanner keeps throwing E_FAIL while the feeder is getting ready to scan the next page, which takes a couple of seconds (it is not a very fast scanner). So, I added a loop to keep trying for 10 seconds, which is a solution that I don't like, but it seems to work.

EDIT 2: This seems to be an issue with the printer's WIA driver. I tried this with a different brand printer and it didn't have this problem at all.

Gradey answered 22/9, 2015 at 4:44 Comment(3)
Vesan are you running this as a service? Or with a different account? make sure that the process has access to the "C:\Users\USER_PROFILE\AppData\Local\Temp\" folder.Sissified
@Sissified I'm running this as a regular WPF application. It does have access to the folder.Gradey
i am struggling on same issue do you find any more solution for fast scanningSubinfeudate
H
2

I'm working on the same thing, and tried to use your code and code from this example: http://www.codeproject.com/Tips/792316/WIA-Scanner-in-Csharp-Windows-Forms

I tested your code on hp scanjet 5590, it only manages to get one image, even when I step through the code in debugger. On second call dialog.ShowTransfer throws ArgumentException with message "Value does not fall within the expected range." I managed to make it work by resetting 'image' and 'dialog' objects and also setting an explicit transfer format ID. Also your code wrote images to the same file. Here is what worked for me if applied to your code:

WIA.CommonDialog dialog = new WIA.CommonDialog();
        WIA.Device device = dialog.ShowSelectDevice(WIA.WiaDeviceType.ScannerDeviceType);
        WIA.Items items = dialog.ShowSelectItems(device);
        foreach (WIA.Item item in items)
        {
            while (true)
            {
                WIA.ImageFile image = null;
                try
                {
                    dialog = new WIA.CommonDialog();
                    image = (WIA.ImageFile)dialog.ShowTransfer(item,"{B96B3CAB-0728-11D3-9D7B-0000F81EF32E}", false);
                    if (image != null && image.FileData != null)
                    {
                        dynamic binaryData = image.FileData.get_BinaryData();
                        if (binaryData is byte[])
                            using (MemoryStream stream = new MemoryStream(binaryData))
                            using (Bitmap bitmap = (Bitmap) Bitmap.FromStream(stream))
                            {
                                bitmap.Save(String.Format(@"C:\Temp\scan{0}.jpg", Path.GetRandomFileName()),
                                    ImageFormat.Jpeg);
                            }
                    }
                }
                catch (COMException)
                {
                    break;
                }
                finally
                {
                    if (image != null)
                        Marshal.FinalReleaseComObject(image);
                }
            }
        }

That CodeProject example failed on my hardware as well, but the same fixes helped. Try it if the code above still doesn't work for you, replace the original Scan method by this:

    public static List<Image> Scan(string scannerId)
    {
        List<Image> images = new List<Image>();

        // select the correct scanner using the provided scannerId parameter
        WIA.DeviceManager manager = new WIA.DeviceManager();
        WIA.Device device = null;
        foreach (WIA.DeviceInfo info in manager.DeviceInfos)
        {
            if (info.DeviceID == scannerId)
            {
                // connect to scanner
                device = info.Connect();
                break;
            }
        }
        // device was not found
        if (device == null)
        {
            // enumerate available devices
            string availableDevices = "";
            foreach (WIA.DeviceInfo info in manager.DeviceInfos)
            {
                availableDevices += info.DeviceID + "\n";
            }

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

        WIA.Item item = null;
        WIA.CommonDialog dialog = new WIA.CommonDialog();
        WIA.Items items = dialog.ShowSelectItems(device);
        if (items == null)
            return images;

        item = items[1];

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

                // save to temp file
                string fileName = Path.GetTempFileName();
                File.Delete(fileName);
                image.SaveFile(fileName);
                try
                {
                    Marshal.FinalReleaseComObject(image);
                }
                finally
                {
                    image = null;
                }
                // add file to output list
                images.Add(Image.FromFile(fileName));
            }
            finally
            {
                //determine if there are any more pages waiting
                WIA.Property documentHandlingSelect = null;
                WIA.Property documentHandlingStatus = null;
                foreach (WIA.Property prop in device.Properties)
                {
                    if (prop.PropertyID == WIA_PROPERTIES.WIA_DPS_DOCUMENT_HANDLING_SELECT)
                        documentHandlingSelect = prop;
                    if (prop.PropertyID == WIA_PROPERTIES.WIA_DPS_DOCUMENT_HANDLING_STATUS)
                        documentHandlingStatus = prop;
                }
                // assume there are no more pages
                hasMorePages = false;
                // may not exist on flatbed scanner but required for feeder
                if (documentHandlingSelect != null)
                {
                    // check for document feeder
                    if ((Convert.ToUInt32(documentHandlingSelect.get_Value()) & WIA_DPS_DOCUMENT_HANDLING_SELECT.FEEDER) != 0)
                    {
                        hasMorePages = ((Convert.ToUInt32(documentHandlingStatus.get_Value()) & WIA_DPS_DOCUMENT_HANDLING_STATUS.FEED_READY) != 0);
                    }
                }
            }
        }
        return images;
    }

Also replace btn_scan_Click method by this:

    private void btn_scan_Click(object sender, EventArgs e)
    {

        try
        {
            //get list of devices available
            List<string> devices = WIAScanner.GetDevices();
            List<Image> images = null;

            //check if device is not available
            if (devices.Count == 0)
            {
                MessageBox.Show("You do not have any WIA devices.");
                this.Close();
            }
            else
            {
                if (devices.Count == 1)
                {
                    images = WIAScanner.Scan(devices[0]);
                }
                else
                {
                    images = WIAScanner.Scan();
                }
            }
            //get images from scanner
            foreach (Image image in images)
            {
                var path = String.Format(@"C:\Temp\scan{0}_{1}.jpg", DateTime.Now.ToString("yyyy-MM-dd HHmmss"), Path.GetRandomFileName());
                image.Save(path, ImageFormat.Jpeg);
            }
        }
        catch (Exception exc)
        {
            MessageBox.Show(exc.Message);
        }
    }
Hardworking answered 30/9, 2015 at 18:54 Comment(2)
Thanks for the suggestions. Unfortunately, the first piece of code didn't work for me (I'm starting to think it must be a problem with the scanner itself). The second one displays the selection dialog before scanning every page, which is not really desirable in my case (the original CodeProject code doesn't display the dialog at all, which is also not acceptable).Gradey
I don't see what significant difference could cause one code work but not the other. It's easy though to stop the codeproject example to stop showing dialogs for each page by moving dialogs out from the loop and not releasing item com object. I updated that code in the original answer.Hardworking
M
-1

This code works for me

     try
     {
        // Create a DeviceManager instance
        var deviceManager = new DeviceManager();

        List<Image> ret = new List<Image>();

        WIA.CommonDialog dialog = new WIA.CommonDialog();
        WIA.Device device = dialog.ShowSelectDevice(WIA.WiaDeviceType.ScannerDeviceType);
        WIA.Items items = dialog.ShowSelectItems(device);

        foreach (WIA.Item item in items)
        {
           while (true)
           {
              try
              {
                 WIA.ImageFile image = (WIA.ImageFile) dialog.ShowTransfer(item);
                 if (image != null && image.FileData != null)
                 {
                    var imageBytes = (byte[]) image.FileData.get_BinaryData();
                    var ms = new MemoryStream(imageBytes);
                    Image img = null;
                    img = Image.FromStream(ms);

                    ret.Add(img);
                 }
              }
              catch
              {
                 break;
              }
           }
        }
        return ret;
     }
     catch (Exception)
     {
        return null;
     }
Muliebrity answered 21/1, 2016 at 17:46 Comment(1)
How is that code different from what I'm doing? Except that it doesn't dispose the MemoryStream and the Image?Gradey

© 2022 - 2024 — McMap. All rights reserved.