有没有人尝试过使用.NET中的TWAIN API调用来扫描TwainDotNet?虽然它通常运行良好,但在与使用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();
}当我们使用BackgroundWorker时,永远不会触发ScanningComplete事件处理程序。有什么建议可以解决这个问题吗?
发布于 2011-12-11 00:33:48
Twain对象在其对象构造函数中需要窗口句柄这一事实表明,Twain对象内部的某些内容需要消息处理。开始时,跨线程消息处理很棘手,但当它发生在API内部时,情况就更复杂了。
如果twain API创建一个窗口句柄(公开创建,如弹出窗口或对话框,或秘密创建,如用于进程间通信(IPC)),作为从后台线程调用的API函数的一部分,则该窗口句柄将绑定到在其上创建它的线程-后台线程。发送到该窗口句柄的所有消息都将排队等待后台线程在消息循环中处理它们。你的后台线程中没有消息循环,所以窗口句柄会被卡住。它不会响应窗口消息。发布的消息将不会得到回复。SendMessage()将会死锁。
即使这不是窗口句柄/消息循环问题,如果Twain API没有显式地、有意地在头脑中实现多线程,那么在跨线程使用时,也很可能会出现问题。您在一个线程中创建twain对象,然后在另一个线程中使用它,因此这是一种跨线程的情况。如果您可以在后台线程中创建twain对象,并且仅在该后台线程的上下文中使用twain对象,则可以解决twain API实现中的线程关联问题。当涉及到窗口句柄和消息时,将所有内容移动到后台线程很可能会使事情变得更糟。
跨线程使用对象的能力不是免费的。如果twain API不是为跨线程使用而设计的,那么您几乎无法使其跨线程工作。最好的办法是将Twain对象保留在主UI线程中。
发布于 2010-02-13 23:19:08
你有没有试过从代码中删除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 > 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没有被触发?这只是一个想法,你能澄清一下吗?
https://stackoverflow.com/questions/2257809
复制相似问题