C# 使用带BackgroundWorker的TWAIN进行TwainDotNet扫描

C# 使用带BackgroundWorker的TWAIN进行TwainDotNet扫描,c#,.net,backgroundworker,twain,C#,.net,Backgroundworker,Twain,有人尝试过使用.NET中的TWAIN API调用进行扫描吗?虽然它通常工作得很好,但当与使用MVVM的WPF应用程序一起使用时,我会遇到一些问题。基本上,我是从一个服务调用Twain扫描函数,而这个服务又使用BackgroundWorker List<BitmapSource> bitmapSources = new List<BitmapSource>(); Twain twain = new Twain(new WpfWindowMessageHook(_window

有人尝试过使用.NET中的TWAIN API调用进行扫描吗?虽然它通常工作得很好,但当与使用MVVM的WPF应用程序一起使用时,我会遇到一些问题。基本上,我是从一个服务调用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();
}
List bitmapSources=new List();
Twain Twain=新Twain(新WpfWindowMessageHook(_窗口));
ScanSettings=new ScanSettings(){ShowTwainUI=false};
使用(BackgroundWorker worker=新BackgroundWorker())
{
worker.DoWork+=(sndr、evnt)=>
{
AutoResetEvent waitHandle=新的AutoResetEvent(假);
EventHandler ScanPleteHandler=(se,ev)=>{waitHandle.Set();};
twain.ScanningComplete+=ScanPleteHandler;
twain.开始扫描(设置);
waitHandle.WaitOne();
如果(twain.Images.Count>0)
{
foreach(twain.Images中的var图像)
{
BitmapSource BitmapSource=Imaging.CreateBitmapSourceFromHBitmap(新位图(图像).GetHbitmap(),
IntPtr.Zero、Int32Rect.Empty、System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions();
添加(bitmapSource);
}
}
};
worker.RunWorkerCompleted+=(sndr,evnt)=>{image1.Source=bitmapSources[0];};
worker.RunWorkerAsync();
}

当我们与BackgroundWorker一起工作时,ScanningComplete事件处理程序从不被激发。有什么解决这个问题的建议吗?

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


您是否在后台工作程序中执行挂钩或类似操作-可能存在交叉线程问题,因此未触发
ScanningComplete
?只是一个想法,你能澄清一下吗?

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

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

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


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

WpfWindowMessageHook需要一个句柄才能工作,因此我传递该值,但为了避免跨线程问题,我将WpfWindowMessageHook中的IntPtr WindowHandle更改为return(IntPtr)window.Dispatcher.Invoke(new Func(()=>interopHelper.handle));这已经解决了您正确预期的跨线程问题。这篇文章是在解决了那个问题后发表的。现在让我试试你的解决方案。嗨,Tommie,你的代码更改也不起作用。我已经删除了BackgroundWorker以使其立即启动。但当以高DPI(600+)处理扫描并启用文档进纸器时,TwainDotNet库内存不足异常。使用ADF扫描时,通常超过3页会引发内存异常。Raj,我从google TwainDotNet问题页面下载了您的示例项目。我想对backgroundworker做同样的事情,因为我想在扫描时在窗口上显示进度/状态。我在访问图像时遇到了同样的问题。但是,在backgroundworker中扫描时,我也无法让窗口做出响应。使用此解决方案进行扫描时,您是否能够获得更新/响应窗口?请让我知道。戴夫,样品溶液应该做得对吗?不。。我想在窗口上放一个进度条,并显示一些反馈。DoWork正在处理扫描。所以,我需要另一个线程来报告进度。我启动了一个Dispatcher并调用:Dispatcher.Invoke(updatePbDelegate,System.Windows.Threading.DispatcherPriority.Background,新对象[]{ProgressBar.ValueProperty,progressBarValue});但是更新进度条的事件在扫描完成后才会触发。如果你有其他想法,请告诉我。[@dthorpe]你的猜测完全正确。TWAIN在一个线程中运行良好,但最好将所有TWAIN调用限制在该线程中,该线程必须具有消息泵,即“UI线程”。
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))