最初は私たちが見るようなイベントバブリングかもしれないと思っていたので、s
という変数がキャッチされたAutomationElement
がハンドラのラムダの中に追加されて、ボタンからも2番目の呼び出しが来るかどうかを示します@Simon Mourierの結果:はい値は同一)、その構成ラベルからではなく、ビジュアルツリーの上下にあるものではありません。
これが除外された後、2つのコールバックのコールスタックを詳しく見ると、スレッド関連の仮説をサポートするものが明らかになりました。 gitからUIAComWrapperをダウンロードし、ソースからコンパイルし、ソースサーバーのシンボルとネイティブをデバッグしました。
これは、最初のコールバックのコールスタックである:
それは原点がメッセージポンプであることを、示しています。 WndProcのコアはフレームワークの非常に厚いレイヤーを泡立てて、ほとんどすべてのWindowsバージョンの要約で、ボタンクラスのOnClick()
ハンドラで終わるまで左マウスのように慎重にデコードします。サブスクライブされたオートメーションイベントが発生し、私たちのラムダに向けられます。これまで予想外だったことはありません。
そして、これは2番目のコールバックでコールスタックは、次のとおりです。
これは、2番目のコールバックがUIAutomationCore
の人工物であることが明らかになりました。 UIスレッドではなくユーザースレッドで実行されます。したがって、購読しているすべてのスレッドがコピーを取得し、UIスレッドは常にそうすることを確実にするメカニズムがあるようです。
残念ながら、ラムダで終わるすべての引数は、1回目と2回目の呼び出しで同じです。可能であればコールスタックを比較することは、タイミング/カウントイベントよりもさらに悪い解決策になります。
しかし:あなたがスレッドでイベントをフィルタ、およびそれらの一方のみを消費することができます出力ウィンドウに
using System;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Automation;
using System.Windows.Interop;
using System.Threading;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs routedEventArgs)
{
IntPtr windowHandle = new WindowInteropHelper(this).Handle;
Task.Run(() =>
{
var element = AutomationElement.FromHandle(windowHandle);
Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent, element, TreeScope.Descendants,
(s, a) =>
{
var ele = s as AutomationElement;
var invokingthread = Thread.CurrentThread;
Debug.WriteLine($"Invoked on {invokingthread.ManagedThreadId} for {ele}, event # {a.EventId.Id}");
/* detect if this is the UI thread or not,
* reference: http://stackoverflow.com/a/14280425/1132334 */
if (System.Windows.Threading.Dispatcher.FromThread(invokingthread) == null)
{
Debug.WriteLine("2nd: this is the event we would be waiting for");
}
else
{
Debug.WriteLine("1st: this is the event raised on the UI thread");
}
});
});
}
private void button_Click(object sender, RoutedEventArgs e)
{
Debug.WriteLine("Clicked!");
}
}
}
結果:
Invoked on 1 for System.Windows.Automation.AutomationElement, event # 20009
1st: this is the event raised on the UI thread
Invoked on 9 for System.Windows.Automation.AutomationElement, event # 20009
2nd: this is the event we would be waiting for
私は問題を再現することができます。しかし、私は別のプロセスからそれを再現することはできません(1つのイベントだけが発生します)。これはバグかもしれませんが、UIAはアウトオブプロセスクライアント向けに設計されています。なぜ同じプロセスからこれをやりたいのですか? –
VSTOで使用します。 ExcelのAPIはすべてのボタンのクリックにアクセスするわけではありません。自動化はこれらの検出にはうまくいくが、2回来る。 – jan
さて、私が見つけることができる唯一の貧弱な解決策は、1)時間イベント、2)ソースが同じオブジェクトであるかどうかを判断すること(イベントソースのGetHashCodeを比較する - これはAutomationElementです - ここで合理的です)3) xミリ秒、それが倍増したと宣言します。 –