4

有没有人尝试过TwainDotNet使用来自 .NET 的 TWAIN API 调用进行扫描?虽然它通常运行良好,但在与使用 MVVM 的 WPF 应用程序一起使用时,我会遇到一些问题。基本上我是从一个服务调用吐温扫描函数,而后者又使用一个 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 事件处理程序。有什么建议可以解决这个问题吗?

4

2 回答 2

6

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

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

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

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

于 2011-12-10T16:33:47.707 回答
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没有被解雇?只是一个想法,你能澄清一下吗?

于 2010-02-13T15:19:07.843 回答