使用 TWAIN 和 BackgroundWorker 进行 TwainDotNet 扫描

发布于 2024-08-22 04:24:59 字数 1436 浏览 9 评论 0原文

有人尝试过 TwainDotNet 通过 .NET 的 TWAIN API 调用进行扫描吗?尽管它通常运行良好,但在与使用 MVVM 的 WPF 应用程序一起使用时,我会遇到一些问题。基本上,我从服务调用 Twain 扫描功能,而服务又使用后台工作程序。

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 事件处理程序永远不会被触发。有什么建议来解决这个问题吗?

Has anyone tried TwainDotNet for scanning with TWAIN API calls from .NET? Though it works well usually I've some issues with it when used along with WPF application using MVVM. Basically I'm calling Twain scanning functions from a Service, which in turn uses a 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();
}

ScanningComplete event handler is never fired when we are working with a BackgroundWorker. Any suggestions to resolve this issue?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

风追烟花雨 2024-08-29 04:24:59

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

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

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

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

The fact that the Twain object requires a window handle in its object constructor suggests that something inside the Twain object requires message handling. Cross-thread message handling is tricky to begin with but even more so when it's happening inside an API.

If the twain API creates a window handle (overtly, such as a popup window or dialog, or secretly, such as for interprocess communication (IPC)) as part of one of the API functions you're calling from the background thread, that window handle will be bound to the thread it was created on - the background thread. All messages sent to that window handle will queue up waiting for the background thread to process them in a message loop. You don't have a message loop in your background thread, so that window handle will get stuck in limbo. It won't respond to window messages. Posted messages will go unanswered. SendMessage() will deadlock.

Even if this is not a window handle / message loop problem, it is very likely that if the Twain API was not explicitly and deliberately implemented with multithreading in mind, it will have problems when used across threads. You are creating the twain object in one thread and then using it in another thread, so this is a cross-thread situation. If you could create the twain object in the background thread and only use the twain object in the context of that background thread, this might work around thread affinity issues in the twain API implementation. When window handles and messages are involved, moving everything to the background thread is just as likely to make things worse.

The ability to use an object across threads does not come for free. If the twain API was not designed for use across threads, there is little you can do to make it work across threads. Your best bet is to keep the Twain object in the main UI thread.

葬花如无物 2024-08-29 04:24:59

您是否尝试过从代码中删除 LINQ 并将其放入一个单独的函数中以首先进行实际测试,请注意,我将其包装在 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 没有被解雇?只是一个想法,你能澄清一下吗?

Have you tried removing the LINQ'ness from the code and put it into a separate function to actually test this out first, note that I have it wrapped up in a try/catch block to see if there's any error, also notice that I created a simple class WorkerArgs for passing the data around as it is non-LINQ code, it would be interesting to see what results there are (if any):

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
   }
}

I couldn't help noticing, it's just after entering the code I noticed this:

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

Are you doing hooking or something like that within the background worker - perhaps there's a cross thread problem hence ScanningComplete is not being fired? Just a thought, Can you clarify anyway?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文