2010-02-13 19 views
4

Est-ce que quelqu'un a essayé TwainDotNet pour numériser avec des appels d'API TWAIN à partir de .NET? Bien que cela fonctionne bien, j'ai quelques problèmes avec l'application WPF utilisant MVVM. Fondamentalement, j'appelle les fonctions de numérisation Twain à partir d'un service, qui à son tour utilise un BackgroundWorker.Numérisation TwainDotNet à l'aide de TWAIN avec 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(); 
} 

Le gestionnaire d'événements ScanningComplete n'est jamais déclenché lorsque nous travaillons avec BackgroundWorker. Des suggestions pour résoudre ce problème?

+0

Raj, j'ai téléchargé votre exemple de projet à partir de la page google TwainDotNet Issues. Je veux faire la même chose avec l'arrière-plan parce que je veux montrer le progrès/état sur la fenêtre pendant la numérisation. J'ai le même problème avec l'accès aux images. cependant, je ne peux pas non plus obtenir la fenêtre pour répondre lors de la numérisation dans l'arrière-plan. avez-vous pu obtenir la fenêtre pour mettre à jour/répondre pendant que vous numérisez avec cette solution? SVP faites le moi savoir. – Dave

+0

Dave, la solution d'échantillon devrait le faire correctement? – Raj

+0

Non .. Je souhaite placer une barre de progression sur la fenêtre et afficher des commentaires. DoWork est en train de traiter l'analyse. Donc, j'ai besoin d'un autre fil pour signaler les progrès. J'ai lancé un DispatcherTimer et appelez: Dispatcher.Invoke (updatePbDelegate, System.Windows.Threading.DispatcherPriority.Background, nouvel objet [] {ProgressBar.ValueProperty, progressBarValue}); mais les événements pour mettre à jour la barre de progression ne se déclenchent qu'après la fin de l'analyse. Si vous avez une autre idée, s'il vous plaît faites le moi savoir. – Dave

Répondre

1

Avez-vous essayé d'enlever la LINQ'ness du code et de le mettre dans une fonction séparée pour tester cela en premier, notez que je l'ai enveloppé dans un bloc try/catch pour voir s'il y a une erreur, notez aussi que J'ai créé une classe simple WorkerArgs pour faire passer les données autour comme il est un code non LINQ, il serait intéressant de voir quels résultats il y a (le cas échéant):

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

Je ne pouvais pas empêcher de remarquer, il est juste après avoir entré le code, j'ai remarqué ceci:

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

Est-ce que vous faites de l'accrochage ou quelque chose comme ça dans l'arrière-plan - peut-être y a-t-il un problème de cross-threads donc ScanningComplete n'est pas viré? Juste une pensée, Pouvez-vous clarifier de toute façon?

+0

WpfWindowMessageHook nécessite un handle pour fonctionner, donc je passe cette valeur, mais pour éviter les problèmes croisés, j'ai changé IntPtr WindowHandle dans WpfWindowMessageHook comme return (IntPtr) _window.Dispatcher.Invoke (new Func (() => _interopHelper.Handle)); Cela a résolu le problème d'interconnexion croisée que vous avez correctement anticipé. Ce post a été fait après avoir réglé ce problème. Laissez-moi essayer votre solution maintenant. – Raj

+0

Salut Tommie, vos changements de code ne fonctionne pas non plus. J'ai supprimé BackgroundWorker pour le configurer pour l'instant. Mais la bibliothèque TwainDotNet ne dispose pas d'une exception de mémoire lors de la gestion des numérisations en haute résolution (600+) et le chargeur de documents est activé. Habituellement plus de 3 pages lorsqu'il est scanné avec ADF lève l'exception de mémoire. – Raj

6

Le fait que l'objet Twain nécessite un handle de fenêtre dans son constructeur d'objet suggère que quelque chose à l'intérieur de l'objet Twain nécessite la gestion des messages. La gestion des messages inter-threads est délicate pour commencer, mais plus encore lorsque cela se passe dans une API. Si l'API twain crée un handle de fenêtre (ouvertement, comme une fenêtre contextuelle ou une boîte de dialogue, ou secrètement, comme pour la communication interprocessus (IPC)) dans le cadre de l'une des fonctions API que vous appelez depuis l'arrière-plan thread, ce handle de fenêtre sera lié au thread sur lequel il a été créé - le thread d'arrière-plan. Tous les messages envoyés à ce handle de fenêtre seront mis en file d'attente en attendant que le thread d'arrière-plan les traite dans une boucle de message. Vous n'avez pas de boucle de message dans votre thread d'arrière-plan, de sorte que le handle de la fenêtre sera bloqué dans les limbes. Il ne répondra pas aux messages de la fenêtre. Les messages postés resteront sans réponse. SendMessage() sera un blocage.

Même s'il ne s'agit pas d'un problème de handle de fenêtre/boucle de message, il est très probable que si l'API Twain n'est pas explicitement et délibérément mise en œuvre, le multithread aura des problèmes. Vous créez l'objet twain dans un thread et ensuite l'utiliser dans un autre thread, donc c'est une situation de threads croisés. Si vous pouviez créer l'objet twain dans le thread d'arrière-plan et utiliser uniquement l'objet twain dans le contexte de ce thread d'arrière-plan, cela pourrait contourner les problèmes d'affinité des threads dans l'implémentation de l'API twain. Lorsque les poignées de fenêtre et les messages sont impliqués, tout déplacer dans le thread d'arrière-plan est tout aussi susceptible d'empirer les choses.

La possibilité d'utiliser un objet sur plusieurs threads ne vient pas gratuitement. Si l'API twain n'a pas été conçue pour être utilisée sur plusieurs threads, il y a peu de choses que vous pouvez faire pour qu'elle fonctionne à travers les threads. Votre meilleur pari est de garder l'objet Twain dans le thread principal de l'interface utilisateur.

+1

[@dthorpe] vos conjectures sont toutes sur place. TWAIN fonctionne correctement dans un thread, mais il est préférable de limiter tous les appels TWAIN à ce thread, et ce thread doit avoir une pompe de message, c'est-à-dire être un 'thread d'interface utilisateur'. – Spike0xff