2010-02-13 56 views
4

有没有人尝试使用TwainDotNet从.NET的TWAIN API调用进行扫描?虽然它运行良好,但通常我在使用MVVM与WPF应用程序一起使用时遇到了一些问题。基本上,我从服务调用Twain扫描函数,而后者使用BackgroundWorker。TwainDotNet使用TWAIN与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事件处理程序永远不会被触发。有任何建议来解决这个问题?

+0

Raj,我从谷歌的TwainDotNet问题页面下载了您的示例项目。我想用backgroundworker做同样的事情,因为我想在扫描时在窗口上显示进度/状态。我在访问图像时遇到同样的问题。不过,我也无法在后台工作人员扫描时看到窗口作出回应。当您使用此解决方案进行扫描时,您是否能够让窗口更新/响应?请让我知道。 – Dave

+0

Dave,样品溶液应该这样做对吗? – Raj

+0

不,我想在窗口上放一个进度条并显示一些反馈。 DoWork正在处理扫描。所以,我需要另一个线程来报告进度。我启动了一个DispatcherTimer并调用: Dispatcher.Invoke(updatePbDelegate,System.Windows.Threading.DispatcherPriority.Background, new object [] {ProgressBar.ValueProperty,progressBarValue}); 但更新进度栏的事件在扫描完成之后才会触发。如果您有其他想法,请告诉我。 – Dave

回答

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未被解雇?只是一个想法,你能澄清吗?

+0

WpfWindowMessageHook需要一个句柄工作,所以我通过该值,但为了避免交叉线程问题我已经将WpfWindowMessageHook中的IntPtr WindowHandle更改为 return(IntPtr)_window.Dispatcher.Invoke(new Func (()=> _interopHelper.Handle)); 这已解决了您已正确预期的跨线程问题。这篇文章是在解决这个问题之后作出的。现在让我试试你的解决方案。 – Raj

+0

嗨汤米,你的代码更改也不起作用。我已经删除了BackgroundWorker来设置它现在。但是,在处理高DPI(600+)以及启用文档进纸器中的扫描时,TwainDotNet库的内存不足异常。使用ADF扫描时,通常超过3页会引发内存异常。 – Raj

6

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

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

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

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

+1

[@dthorpe]你的猜测是全部点亮的。 TWAIN在一个线程中工作正常,但最好将所有TWAIN调用限制在该线程中,并且该线程必须具有消息泵,即“UI线程”。 – Spike0xff