TwainDotNet Scan Using TWAIN with BackgroundWorker

Has anyone tried TwainDotNet to scan using .NET TWAIN API calls? Although it works well, as a rule, I have some problems with it when used together with a WPF application using MVVM. Basically, I call Twain scanning functions from the Service, which in turn uses 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(); } 

The ScanningComplete event handler never starts when we work with BackgroundWorker. Any suggestions to fix this problem?

+4
source share
2 answers

The fact that a Twain object requires a window handle in its object constructor suggests that something inside the Twain object requires message processing. Handling end-to-end messages is complicated, but even more so when it happens inside the API.

If the twain API creates a window handle (open, for example, a pop-up window or dialog, or secretly, for example, for interprocess communication (IPC)) as part of one of the API functions that you call from the background thread, this window handle will be associated with the thread that was created on - background thread. All messages sent to this window handle will be queued, waiting for the background thread to process them in the message loop. You do not have a message loop in the background thread, so the window handle will be stuck in limbo. It will not respond to window messages. Sent messages will remain unanswered. SendMessage () will be blocked.

Even if this is not a window descriptor / message problem, it is very likely that if the Twain API was not explicitly and deliberately implemented taking into account multithreading, it will have problems when used in streams. You create a twain object in one thread and then use it in another thread, so this is a cross-thread situation. If you could create a twain object in a background thread and use only a twain object in the context of this background thread, this could bypass the thread proximity problems in the twain API implementation. When it comes to descriptors and messages, moving everything into the background thread also makes the situation worse.

The ability to use the facility in streams is not provided for free. If the twain API was not intended to be used in streams, you can do this to make it work in streams. It is best to keep the Twain object in the main user interface thread.

+6
source

You tried to remove LINQ'ness from the code and put it in a separate function in order to actually check this first, note that I wrapped it in a try/catch to see if there is any error, and also notice that I created a simple WorkerArgs class to transfer data around, since this is non-LINQ code, it would be interesting to see what the results 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 &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 } } 

I could not help but notice that right after entering the code I noticed this:

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

Are you doing a hook or something similar in the background worker - maybe there is a crossflow problem, so the ScanningComplete does not start? Just a thought, can you still clarify?

+1
source

Source: https://habr.com/ru/post/1301194/


All Articles