使用WIA自动进纸扫描仪进行扫描第二页失败

7

我正在尝试使用具有自动进纸器的扫描仪扫描多个页面。目前我的代码非常简单:

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

我尝试查询WIA_DPS_DOCUMENT_HANDLING_STATUS属性以查看进纸器中是否有任何页面可用,但它总是返回1,因此我捕捉到一个COM异常,如果我得到WIA_ERROR_PAPER_EMPTY,我知道文档进纸器为空。
问题在于这段代码只扫描第一页,当再次调用dialog.ShowTransfer方法时,我会得到一个带有E_FAIL HResult的异常,我无法再扫描更多页面。奇怪的是,当我在调试器中逐步执行这段代码时,一切正常,所有页面都被扫描了。
我尝试通过进行Marshal.ReleaseComObject(image)image = null来释放图像对象,但这没有帮助。也没有这个问题的建议。我是否做错了什么导致这些错误?
编辑:我找不到一个好的解决方案。扫描仪在准备扫描下一页时会抛出E_FAIL,需要几秒钟(它不是一个非常快的扫描仪)。所以,我添加了一个循环尝试10秒钟,这是我不喜欢的解决方案,但它似乎可以工作。
编辑2:这似乎是打印机的WIA驱动程序的问题。我尝试了不同品牌的打印机,它完全没有这个问题。

Vesan,您正在运行此服务吗?或者使用不同的账户?请确保该进程可以访问“C:\Users\USER_PROFILE\AppData\Local\Temp"文件夹。 - Aldracor
@Aldracor 我正在运行这个作为常规的WPF应用程序。它确实可以访问该文件夹。 - vesan
我正在为同样的问题苦苦挣扎,你有没有找到更快速扫描的解决方案? - Mukesh Methaniya
2个回答

2
我正在处理与此相关的事情,并尝试使用您的代码以及来自此示例的代码:http://www.codeproject.com/Tips/792316/WIA-Scanner-in-Csharp-Windows-Forms
我在惠普扫描仪5590上测试了您的代码,它只能获取一张图像,即使我通过调试器逐步执行代码也是如此。第二次调用dialog.ShowTransfer时,会抛出ArgumentException并显示消息“值不在预期范围内”。我设法通过重置'image'和'dialog'对象并设置显式传输格式ID来使它正常工作。另外,您的代码将图像写入同一文件。如果将以下内容应用于您的代码,则可解决问题:
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);
                }
            }
        }

那个CodeProject的例子在我的硬件上也失败了,但是同样的修复方法有效。如果上面的代码仍然无法工作,请尝试将原始的 Scan 方法替换为以下内容:

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

同时将btn_scan_Click方法替换为以下内容:

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

感谢您的建议。不幸的是,第一段代码对我没有起作用(我开始认为这可能是扫描仪本身的问题)。第二个代码在每一页扫描之前都会显示选择对话框,这在我的情况下并不理想(原始的CodeProject代码根本不显示对话框,这也是不可接受的)。 - vesan
我不明白有什么重大的差异会导致一个代码可以工作而另一个不能。不过,通过将对话框移出循环并且不释放项目COM对象,可以轻松地停止codeproject示例在每个页面上显示对话框。我已经在原始答案中更新了该代码。 - shuribot

-1

这段代码对我有效

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

这段代码与我正在做的有何不同?除了它没有释放 MemoryStreamImage - vesan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接