पहले मैंने सोचा था कि यह किसी प्रकार का इवेंट बबलिंग हो सकता है, इसलिए के साथ एक चर के साथ AutomationElement
के रूप में डाला गया हैडलर लैम्ब्डा के अंदर जोड़ा गया है यह दिखाने के लिए कि दूसरा आक्रमण बटन से भी आता है (टिप्पणी के अनुसार @ सिमन मॉरीयर का, परिणाम: हां मान समान हैं) और इसके घटक लेबल से नहीं, या दृश्य पेड़ को ऊपर या नीचे कुछ भी नहीं।
इसके बाद इनकार किया गया था, दो कॉलबैक के कॉल स्टैक पर एक नज़र डालने से कुछ पता चला, जो धागे से संबंधित परिकल्पना का समर्थन करता है। मैंने स्रोत से संकलित गिट से UIAComWrapper डाउनलोड किया और स्रोत सर्वर प्रतीकों और मूल के साथ डीबग किया।
यह पहली कॉलबैक में कॉल स्टैक है:
इससे पता चलता है, कि उद्गम स्थल संदेश पंप है। मुख्य WndProc फ्रेमवर्क सामग्री की उस अविश्वसनीय रूप से मोटी परत के माध्यम से इसे बुलबुले करता है, लगभग सभी विंडोज संस्करणों के पुनर्मूल्यांकन में, इसे बाएं माउस अप के रूप में डिकोडिंग करते हुए, जब तक यह बटन वर्ग के OnClick()
हैंडलर में समाप्त होता है, जहां से सब्सक्राइब किया गया स्वचालन कार्यक्रम उठाया जाता है और हमारे लामा की तरफ निर्देशित किया जाता है। अभी तक अप्रत्याशित कुछ भी नहीं।
और यह दूसरा कॉलबैक में कॉल स्टैक है:
यह पता चलता है कि दूसरा कॉलबैक UIAutomationCore
का एक विरूपण साक्ष्य है। और: यह यूआई थ्रेड पर नहीं, उपयोगकर्ता थ्रेड पर चलता है। तो स्पष्ट रूप से एक तंत्र है जो सुनिश्चित करता है कि सब्सक्राइब किए गए प्रत्येक थ्रेड को प्रतिलिपि मिलती है, और यूआई थ्रेड हमेशा करता है।
दुर्भाग्यवश, लैम्बडा में समाप्त होने वाले सभी तर्क, पहले और दूसरे कॉल के समान हैं। और कॉल स्टैक्स की तुलना करना, यद्यपि संभव है, समय/गिनती घटनाओं से भी एक समाधान होगा।
लेकिन: आप धागा द्वारा घटनाओं फ़िल्टर कर सकते हैं, और उनमें से केवल एक का उपभोग: उत्पादन विंडो में
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
मैं समस्या को पुन: कर सकते हैं। हालांकि, मैं इसे किसी अन्य प्रक्रिया से पुन: उत्पन्न नहीं कर सकता (केवल एक घटना उठाई जाती है)। यह एक बग हो सकता है, लेकिन यूआईए को आउट-ऑफ-प्रोसेस क्लाइंट के लिए डिज़ाइन किया गया था, आप इसे उसी प्रक्रिया से क्यों करना चाहते हैं? –
वीएसटीओ के साथ उपयोग के लिए। एक्सेल एपीआई आपको सभी बटन क्लिक तक पहुंच नहीं देता है। स्वचालन इनका पता लगाने के लिए अच्छी तरह से काम करता है, लेकिन मुर्गी वे दो बार आते हैं। – jan
ठीक है। खैर, मुझे लगता है कि एकमात्र खराब समाधान 1) समय घटनाओं, 2) निर्धारित करता है कि स्रोत एक ही वस्तु है (घटना स्रोत के गेटहाशकोड की तुलना करना - जो एक स्वचालन एलीमेंट है - यहां उचित लगता है) 3) यदि दो घटनाएं होती हैं एक्स मिलीसेकंड, फिर घोषणा करें कि यह दोगुना हो गया है। –