使用TWAIN和BackgroundWorker进行TwainDotNet扫描

时间:2010-02-13 14:13:00

标签: c# .net backgroundworker twain

是否有人尝试使用TwainDotNet来扫描来自.NET的TWAIN API调用?虽然它运行良好,但是当与使用MVVM的WPF应用程序一起使用时,我遇到了一些问题。基本上我是从服务调用Twain扫描函数,而Service又使用BackgroundWorker。

List<BitmapSource> bitmapSources = new List<BitmapSource>();
Twain twain = new Twain(new WpfWindowMessageHook(_window));
ScanSettings settings = new ScanSettings() { ShowTwainUI = false };
using (BackgroundWorker worker = new BackgroundWorker())
{
    worker.DoWork += (sndr, evnt) =>
    {
        AutoResetEvent waitHandle = new AutoResetEvent(false);
        EventHandler scanCompleteHandler = (se, ev) => { waitHandle.Set(); };
        twain.ScanningComplete += scanCompleteHandler;
        twain.StartScanning(settings);
        waitHandle.WaitOne();

        if (twain.Images.Count > 0)
        {
            foreach (var image in twain.Images)
            {
                BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(new Bitmap(image).GetHbitmap(),
                    IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
                bitmapSources.Add(bitmapSource);
            }
        }
    };
    worker.RunWorkerCompleted += (sndr, evnt) => { image1.Source = bitmapSources[0]; };
    worker.RunWorkerAsync();
}

当我们使用BackgroundWorker时,永远不会触发ScanningComplete事件处理程序。有什么建议可以解决这个问题吗?

2 个答案:

答案 0 :(得分:6)

Twain对象在其对象构造函数中需要窗口句柄这一事实表明Twain对象内部的某些内容需要处理消息。开始时跨线程消息处理很棘手,但是当它在API内部发生时更是如此。

如果twain API创建一个窗口句柄(明显地,例如弹出窗口或对话框,或秘密地,例如进程间通信(IPC))作为您从后台线程调用的API函数的一部分,该窗口句柄将绑定到它创建的线程 - 后台线程。发送到该窗口句柄的所有消息将排队等待后台线程在消息循环中处理它们。您的后台线程中没有消息循环,因此窗口句柄将陷入困境。它不会响应窗口消息。发布的消息将无法回复。 SendMessage()将死锁。

即使这不是一个窗口句柄/消息循环问题,如果Twain API没有明确地故意实现并考虑到多线程,那么很可能在跨线程使用时会遇到问题。您正在一个线程中创建twain对象,然后在另一个线程中使用它,因此这是一个跨线程的情况。如果您可以在后台线程中创建twain对象并且仅在该后台线程的上下文中使用twain对象,则这可能会解决twain API实现中的线程关联性问题。当涉及窗口句柄和消息时,将所有内容移动到后台线程也可能会使事情变得更糟。

跨线程使用对象的能力不是免费的。如果twain API不是为跨线程使用而设计的,那么你几乎无法使它跨线程工作。最好的办法是将Twain对象保留在主UI线程中。

答案 1 :(得分:1)

您是否尝试从代码中删除LINQ'ness并将其放入单独的函数中以实际测试它,请注意我将其包含在try/catch块中以查看是否存在任何错误,还注意到我创建了一个简单的类WorkerArgs来传递数据,因为它是非LINQ代码,看看有什么结果(如果有的话)会很有趣:

public class WorkerArgs{
   public List<BitMapSource> _bitmapSources;
   public Twain _twain;
   public ScanSettings _settings;
}
List<BitmapSource> bitmapSources = new List<BitmapSource>();
Twain twain = new Twain(new WpfWindowMessageHook(_window));
ScanSettings settings = new ScanSettings() { ShowTwainUI = false };
WorkerArgs wArgs = new WorkerArgs();
wArgs._bitmapSources = bitmapSources;
wArgs._twain = twain;
wArgs._settings = settings;
using (BackgroundWorker worker = new BackgroundWorker())
{
    worker.DoWork += new DoWorkEventHandler(worker_DoWork);
    worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
    worker.RunWorkerAsync((WorkerArgs)wArgs);
}

void  worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
   try{
    image1.Source = (WorkerArgs(e.Argument))._bitmapSources[0];
   }catch(Exception up){
     throw up; // :P
   }
}

void  worker_DoWork(object sender, DoWorkEventArgs e)
{
   try{
     WorkerArgs thisArgs = (WorkerArgs)e.Argument as WorkerArgs;
     if (thisArgs != null){
        AutoResetEvent waitHandle = new AutoResetEvent(false);
        EventHandler scanCompleteHandler = (se, ev) => { waitHandle.Set(); };
        thisArgs._twain.ScanningComplete += scanCompleteHandler;
        thisArgs._twain.StartScanning(settings);
        waitHandle.WaitOne();

        if (thisArgs._twain.Images.Count &gt; 0)
        {
            foreach (var image in twain.Images)
            {
                BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(new Bitmap(image).GetHbitmap(),
                    IntPtr.Zero, Int32Rect.Empty, System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
                thisArgs._bitmapSources.Add(bitmapSource);
            }
        }
    }
   }catch(Exception up){
     throw up; // :P
   }
}

我忍不住注意到,只是在输入代码后我注意到了这一点:

Twain twain = new Twain(new WpfWindowMessageHook(_window))

你是否正在后台工作人员中进行挂钩或类似事情 - 也许有一个交叉线程问题,因此ScanningComplete没有被解雇?只是一个想法,你能澄清一下吗?