4

クリップボードのデータが変更されるたびに検出しようとしています。ということで、タイマーをセットして継続的に変化をチェックするようにしていClipboard.GetText()ます。

私は次のコードを使用しています:

public void WaitForNewClipboardData()
{
    //This is in WPF, Timer comes from System.Timers
    Timer timer = new Timer(100);
    timer.Elapsed += new ElapsedEventHandler(
        delegate(object a, ElapsedEventArgs b){
            if (Clipboard.GetText() != ClipBoardData)
            {
                SelectedText.Text = Clipboard.GetText();
                ClipBoardData = Clipboard.GetText();
                timer.Stop();
            }
        });
    timer.Start();
}

実行すると次のエラーが表示されます。

OLE 呼び出しを行う前に、現在のスレッドをシングルスレッド アパートメント (STA) モードに設定する必要があります。

誰かが理由を知っていますか?

4

5 に答える 5

3

メソッドは Clipboard クラスにアクセスします。これは、呼び出し元が STA モードである必要がある OLE 呼び出しです。ここでの問題は、別のスレッドで動作するタイマーにある可能性が最も高いです。これに関する詳細については、次のリンクを参照してください。

http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/2411f889-8e30-4a6d-9e28-8a46e66c0fdb/

また、Windows イベントを利用してクリップボードを監視する方法に関する完全な記事へのリンクは次のとおりです。

http://www.radsoftware.com.au/articles/clipboardmonitor.aspx

この記事では、クリップボードをより適切に監視する方法についていくつかのヒントを提供し、この問題を回避できると思います. エラーが発生した理由を知ることはまだ良いことですが、このタスクを実行するためのより良い方法があります。

于 2011-05-30T05:50:57.007 に答える
2

タイマーを使用してクリップボードをポーリングすることは、非常に悪い習慣です。クリップボードは共有リソースであり、クリップボードを監視している他のアプリに干渉します (適切なクリップボード通知を介して、つまりルールに従います)。そして、ユーザーがしようとしていることは何でも衝突します。たとえば、ユーザーがデータをクリップボードにコピーしようとして、それをポーリング ループで開いて調べた場合、「クリップボードを開けません」というエラーまたはクラッシュが発生します。MSDN のクリップボード ビューアを参照してください: http://msdn.microsoft.com/en-us/library/ff468802(v=VS.85).aspx

于 2011-05-30T14:49:16.907 に答える
2

これは、基本的にマネージド Windows GUI アプリケーションのスレッド モデルです。UI スレッドとインターフェイスするコードを実行しているスレッドは、まったく同じでなければなりません。起動パスの SynchronizationContext をどこかで維持している場合 (これを静的変数に入れることができます)、そこにメッセージを投稿できます。これらのメッセージは最終的に正しいスレッドで実行されるため、このエラーは発生しません。

public partial class App : Application
{
    public static SynchronizationContext SynchronizationContext;

    protected override void OnStartup(StartupEventArgs e)
    {
        // This is my preferred way of accessing the correct SynchronizationContext in a WPF app
        SynchronizationContext = SynchronizationContext.Current;

        base.OnStartup(e);

        var mainWindow = MainWindow;

        var t = new Thread(() => {
            Thread.Sleep(3000);

            SynchronizationContext.Post(state => {
                mainWindow.Hide(); // this will not throw an exception
            }, null);

            mainWindow.Close(); // this will throw an exception
        });

        t.Start();
    }
}

したがって、基本的に、異なるスレッド (つまり、タイマーなど) を使用する場合は、元の起動スレッドが特別であることを覚えておく必要があります (それが STA スレッドであることを考えると)。その特別なスレッドに属するオブジェクトでメソッドを呼び出すには、App クラスの静的メンバーとして提供した SynchronizationContext を使用します。

また、実際にメイン UI スレッドにディスパッチするタイマーを使用することを検討することもできます。そうすれば、自分で SynchronizationContext に投稿するという面倒な作業を行う必要がなくなります。

于 2011-05-30T05:50:03.697 に答える
1

これについてはよくわかりませんが、テキストの変更を呼び出してみましたか? まったく同じエラーが発生し (シナリオはまったく異なります)、コントロール プロパティを変更するメソッドを呼び出して解決しました。

文字列パラメーターを使用してデリゲートを作成しました。

public delegate void TextBoxChangeDelegate(string text);

次に、実際の変更を行うメソッド:

void TextBoxChange(string text)
{
   MyTextBox.Text = text;
}

次に、スレッド プロセスでこのメソッドを呼び出します (この場合は、タイマー イベント)。

public void ThreadService()
{
  while(Running)
  {
    Invoke(new TextBoxChangeDelegate(TextBoxChange), new object[] { "New Value: "+ strNewValue });
  }

}

これはWinFormsにありました。UI スレッド以外のスレッドでコントロール プロパティを変更すると問題が発生することを初めて知りました。これがあなたがしようとしているものにさえ近くない場合は申し訳ありません。

于 2011-05-30T09:15:44.693 に答える
0

スレッドデリゲートでウィンドウコントロールを使用できないため、基本的にタイマーはスレッドであり、ウィンドウコントロールを使用するデリゲートを渡すと問題が発生します。これが役立つかどうかを確認して ください

于 2011-05-30T05:51:49.690 に答える