4

.NET からの TWAIN API 呼び出しでスキャンするためにTwainDotNetを試した人はいますか? 通常はうまく機能しますが、MVVMを使用するWPFアプリケーションと一緒に使用すると、いくつかの問題があります。基本的に、Service から Twain スキャン関数を呼び出しています。Service は 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 イベント ハンドラーが起動されることはありません。この問題を解決するための提案はありますか?

4

2 に答える 2

6

Twain オブジェクトがそのオブジェクト コンストラクターでウィンドウ ハンドルを必要とするという事実は、Twain オブジェクト内の何かでメッセージ処理が必要であることを示唆しています。クロススレッド メッセージの処理は、最初は難しいですが、API 内で行われている場合はなおさらです。

バックグラウンド スレッドから呼び出している API 関数の 1 つの一部として、twain API がウィンドウ ハンドル (ポップアップ ウィンドウやダイアログなどで明示的に、またはプロセス間通信 (IPC) などで密かに) を作成する場合、そのウィンドウハンドルは、それが作成されたスレッド (バックグラウンド スレッド) にバインドされます。そのウィンドウ ハンドルに送信されたすべてのメッセージは、バックグラウンド スレッドがメッセージ ループで処理するのを待ってキューに入れられます。バックグラウンド スレッドにメッセージ ループがないため、ウィンドウ ハンドルが行き詰まります。ウィンドウ メッセージには応答しません。投稿されたメッセージは返信されません。SendMessage() はデッドロックします。

これがウィンドウ ハンドル/メッセージ ループの問題ではない場合でも、Twain API がマルチスレッドを念頭に置いて明示的かつ意図的に実装されていない場合、スレッド間で使用すると問題が発生する可能性が非常に高くなります。あるスレッドで twain オブジェクトを作成し、それを別のスレッドで使用しているため、これはクロススレッドの状況です。バックグラウンド スレッドで twain オブジェクトを作成し、そのバックグラウンド スレッドのコンテキストでのみ twain オブジェクトを使用できる場合は、twain API 実装でのスレッド アフィニティの問題を回避できる可能性があります。ウィンドウ ハンドルとメッセージが関係している場合、すべてをバックグラウンド スレッドに移動すると、状況が悪化する可能性があります。

スレッド間でオブジェクトを使用する機能は無料では提供されません。twain API がスレッド間で使用するように設計されていない場合、スレッド間で動作させるためにできることはほとんどありません。最善の策は、Twain オブジェクトをメイン UI スレッドに保持することです。

于 2011-12-10T16:33:47.707 に答える
1

コードから 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 &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))

バックグラウンドワーカー内でフックなどを行っていますか?おそらくクロススレッドの問題が発生しているためScanningComplete、起動されていませんか? 考えただけですが、とにかく明確にしていただけますか?

于 2010-02-13T15:19:07.843 に答える