如何从文档进纸器异步扫描和传输图像
与 TWAIN 通信的哪些部分可以放入另一个线程中,例如BackgroundWorker? 或者: 是否可以拆分处理图像传输的循环?
某些扫描仪驱动程序在返回到调用应用程序之前会扫描所有图像,这会强制应用程序立即处理所有图像。当突然必须立即处理所有事件(在每个扫描图像后引发)时,这会导致我的 WPF 应用程序中出现 OutOfMemoryException 或奇怪的行为。此外,应用程序将挂起,直到传输完成。
我正在使用 TwainDotNet:http://code.google.com/p/twaindotnet/ 但是我还在寻找一个通用的解决方案来描述消息过滤器以及与独立于 TwainDotNet 的 TWAIN 的交互。包含 TWAIN 消息的工作流程就足够了。也欢迎其他语言,最好是 C 或 Deplhi。
DataSourceManager 中消息过滤器的当前实现可以描述如下:
- 从窗口句柄 (hwnd) 获取消息信息
- 则将内容发送到 TWAIN 等
- 复杂的过滤器,如果消息关闭(例如,按下 TWAIN UI 中的取消按钮时),
- 关闭数据源
- 禁用过滤器
- 调用 ScanningComplete 事件
- 如果消息传输就绪,则 :
- 在循环中(直到 ADF 为空等,这会阻止消息过滤器)
- 获取图像
- 将图像指针转换为 GDI+ 图像
- 以图像作为参数调用 TransferImage 事件
- 重置传输
- 关闭数据源等(与消息关闭相同)
- 通知窗口,该消息已被处理
我已经用几台扫描仪对此进行了测试:
- Fujitsu fi-5120C 调用 TransferImage每次传输页面时都会发生事件。该图像立即弹出在我的 WPF 应用程序的图像列表中。
- Canon DR-5010C 会阻止我的 WPF 应用程序,直到扫描完所有图像(直到循环结束)。 Windows 甚至说,WPF 应用程序没有响应。所有图像传输完毕后,只显示很少的图像,并且图像列表中的选择闪烁等。
我不关心显示问题,而是担心窗口被阻塞和内存问题。将将图像传输到BackgroundWorker的循环导致了几次崩溃,我无法调试。我当然考虑了 WPF 的线程问题。 我也不知道如何分割传输循环,以便在传输一张图像后,程序返回到消息过滤器,并且可以将消息标记为已处理。
Which parts of the communication with TWAIN can be put into another thread, e.g. a BackgroundWorker?
Or:
Is it possible to split the loop that handles the image transfer?
Some scanner drivers scan all images before returning to the calling application which forces the application to handle all images at once. This results in e.g. OutOfMemoryException or weird behavior in my WPF application when suddenly all events (raised after every scanned image) have to be handled at once. Additionally the application hangs until the transfer was completed.
I am using TwainDotNet: http://code.google.com/p/twaindotnet/ but I'm also looking for a generic solution describing the message filter and the interaction with TWAIN independant of TwainDotNet. A workflow containing the TWAIN messages would suffice. Other languages are also welcome, preferrable something like C or Deplhi.
The current implementation of the message filter in DataSourceManager can be described as following:
- Get message info from window handle (hwnd)
- Complicate filter, send stuff to TWAIN etc.
- if message close (e.g. when pressing the cancel button in the TWAIN UI)
- Close data source
- Disable filter
- Call ScanningComplete event
- if message transfer ready:
- In a loop (until the ADF is empty etc., this blocks the message filter)
- Get image
- Convert image pointer to GDI+ image
- Call TransferImage event with image as parameter
- Reset transfer
- Close data source etc. (same as message close)
- Notify windows, that the message has been handled
I've tested this with several scanners:
- A Fujitsu fi-5120C calls the TransferImage event every time a page has been transferred. The image pops up immediately in an image list in my WPF application.
- A Canon DR-5010C blocks my WPF application until all images have been scanned (until the loop ends). Windows even says, the WPF application is not responding. After all images have been transferred, only few images are displayed and the selection in the image list flickers etc..
I am not concerned about the display problems, but rather about the blocked window and the memory problems. Putting the loop that transfers the images into a BackgroundWorker caused several crashes, that I could not debug. Of cause I considered the threading issues of WPF.
I also don't know how to split the transfer loop, so that, after transferring one image, the program returns to the message filter and the message can be marked as handled.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我在 Atalasoft 工作,但我不太了解 WPF,甚至不太了解 DotTwain!
我可以告诉你,一般 TWAIN 扫描可以在单独的扫描线程上完成,但你必须小心。最简单的方法是在扫描线程上执行所有 TWAIN 操作 - 不要在两个线程之间混合 TWAIN 调用。
扫描线程必须具有消息泵或“UI”线程,无论您的环境如何。它不仅仅是一个工作线程。
TWAIN 期望获得一个窗口句柄(老式的 Win32 HWND)来用作扫描仪 UI 的父窗口。我建议为此目的在扫描线程上创建一个“扫描父”窗口。您可以根据自己的选择使其可见或不可见,并在扫描作业结束时销毁它。
如果您的扫描作业可能非常大(例如 50 页 400 DPI 颜色),您必须确保扫描过程不会填满逻辑内存或 RAM。如果您填满了逻辑内存(32 位 Windows 进程需要大约 2GB 的地址空间来使用),分配将会失败。如果填满 RAM,则消耗/处理传入图像的代码可能会开始交换,从而从根本上减慢速度,然后扫描提前运行并填满逻辑内存。因此,您需要:
线程,或
不能在处理/处置之前运行太远。
我通常发现我希望能够取消扫描线程,这需要一些耐心,因为 TWAIN 调用不能被中断,而且其中一些调用很繁重。正如您在佳能上注意到的那样。另一方面,如果您强制终止 TWAIN 调用中的线程,扫描仪可能需要重新启动电源,甚至需要重新启动系统,并且 TWAIN 本身将阻塞,直到 TWAIN 管理器 DLL 从内存中卸载并重新加载。通常最好很有礼貌地关闭 TWAIN。
I work for Atalasoft, but I don't know WPF, or even that much about DotTwain!
I can tell you that generally TWAIN scanning can be done on a separate scanning thread, but you have to take some care. The simplest approach is to do all TWAIN operations on the scanning thread - don't mix TWAIN calls between two threads.
The scanning thread has to have a message pump or be a 'UI' thread, whatever that takes in your environment. It is not just a worker thread.
TWAIN expects to be given a window handle (old-fashioned Win32 HWND) to use as the parent window for the scanner's UI. I recommend creating a 'scanning parent' window for this purpose, on the scanning thread. You can make it visible or not as you choose, and destroy it at the end of the scan job.
If your scan jobs can be very big (e.g. 50 pages of 400 DPI color) you have to make sure the scanning process does not fill up either logical memory or RAM. If you fill up logical memory (a 32-bit Windows process gets about 2GB of address space to work with) allocations will fail. If you fill up RAM, the code that is consuming/disposing of the incoming images may start to swap, slowing down radically, then scanning runs ahead and fills up logical memory. So you need to either:
thread, or
cannot run too far ahead of their processing/disposition.
I usually find I want to be able to cancel the scan thread, which requires some patience since TWAIN calls cannot be interrupted, and some of them are heavy. As you've noticed with your Canon. On the other hand if you force-kill a thread inside a TWAIN call, the scanner may require a power-cycle or even a system restart, and TWAIN itself will block until the TWAIN manager DLL is unloaded from memory and reloaded. Usually best to shut down TWAIN very politely.