2017-01-18 10 views
7

मुझे अंदरूनी प्रक्रिया से स्वचालन घटनाओं को सुनने में परेशानी हो रही है। मैंने नीचे एक नमूना लिखा है जहां मेरे पास एक बटन के साथ एक सरल WPF एप्लिकेशन है। ट्रीस्कोप के साथ खिड़की पर Invoke घटना के लिए एक स्वचालन हैंडलर जोड़ा जाता है: descendants।यूआई ऑटोमेशन घटनाओं को दो बार उठाया गया है

Invoked:20009 
Clicked! 
Invoked:20009 

क्यों लागू घटना दो बार नियंत्रित किया जाता है:

public MainWindow() 
{ 
    InitializeComponent(); 

    Loaded += OnLoaded; 
} 

private void OnLoaded(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) => 
      { 
       Debug.WriteLine($"Invoked:{a.EventId.Id}"); 
      }); 

    }); 
} 

private void button_Click(object sender, RoutedEventArgs e) 
{ 
    Debug.WriteLine("Clicked!"); 
} 

जब मैं बटन पर क्लिक करें, यह मैं क्या मिलता है?

यदि मैं कार्य को हटा देता हूं। रुन मैं केवल इसे एक बार प्राप्त करता हूं जैसा कि मैं चाहता हूं, लेकिन मैंने कई स्थानों को पढ़ा है जिन्हें आपको यूआई थ्रेड (जैसे https://msdn.microsoft.com/en-us/library/ms788709(v=vs.110).aspx) से स्वचालन कोड नहीं कहना चाहिए। असली कोड में ऐसा करने के लिए मेरे लिए भी अव्यवहारिक है।

मैं इस नमूने में UIAComWrapper लाइब्रेरी का उपयोग करता हूं, लेकिन मुझे UIAutomationClient लाइब्रेरी के प्रबंधित और COM संस्करण दोनों के साथ समान व्यवहार मिलता है।

+0

मैं समस्या को पुन: कर सकते हैं। हालांकि, मैं इसे किसी अन्य प्रक्रिया से पुन: उत्पन्न नहीं कर सकता (केवल एक घटना उठाई जाती है)। यह एक बग हो सकता है, लेकिन यूआईए को आउट-ऑफ-प्रोसेस क्लाइंट के लिए डिज़ाइन किया गया था, आप इसे उसी प्रक्रिया से क्यों करना चाहते हैं? –

+1

वीएसटीओ के साथ उपयोग के लिए। एक्सेल एपीआई आपको सभी बटन क्लिक तक पहुंच नहीं देता है। स्वचालन इनका पता लगाने के लिए अच्छी तरह से काम करता है, लेकिन मुर्गी वे दो बार आते हैं। – jan

+0

ठीक है। खैर, मुझे लगता है कि एकमात्र खराब समाधान 1) समय घटनाओं, 2) निर्धारित करता है कि स्रोत एक ही वस्तु है (घटना स्रोत के गेटहाशकोड की तुलना करना - जो एक स्वचालन एलीमेंट है - यहां उचित लगता है) 3) यदि दो घटनाएं होती हैं एक्स मिलीसेकंड, फिर घोषणा करें कि यह दोगुना हो गया है। –

उत्तर

1

पहले मैंने सोचा था कि यह किसी प्रकार का इवेंट बबलिंग हो सकता है, इसलिए के साथ एक चर के साथ AutomationElement के रूप में डाला गया हैडलर लैम्ब्डा के अंदर जोड़ा गया है यह दिखाने के लिए कि दूसरा आक्रमण बटन से भी आता है (टिप्पणी के अनुसार @ सिमन मॉरीयर का, परिणाम: हां मान समान हैं) और इसके घटक लेबल से नहीं, या दृश्य पेड़ को ऊपर या नीचे कुछ भी नहीं।

इसके बाद इनकार किया गया था, दो कॉलबैक के कॉल स्टैक पर एक नज़र डालने से कुछ पता चला, जो धागे से संबंधित परिकल्पना का समर्थन करता है। मैंने स्रोत से संकलित गिट से UIAComWrapper डाउनलोड किया और स्रोत सर्वर प्रतीकों और मूल के साथ डीबग किया।

यह पहली कॉलबैक में कॉल स्टैक है:

call stack in first invokation

इससे पता चलता है, कि उद्गम स्थल संदेश पंप है। मुख्य WndProc फ्रेमवर्क सामग्री की उस अविश्वसनीय रूप से मोटी परत के माध्यम से इसे बुलबुले करता है, लगभग सभी विंडोज संस्करणों के पुनर्मूल्यांकन में, इसे बाएं माउस अप के रूप में डिकोडिंग करते हुए, जब तक यह बटन वर्ग के OnClick() हैंडलर में समाप्त होता है, जहां से सब्सक्राइब किया गया स्वचालन कार्यक्रम उठाया जाता है और हमारे लामा की तरफ निर्देशित किया जाता है। अभी तक अप्रत्याशित कुछ भी नहीं।

और यह दूसरा कॉलबैक में कॉल स्टैक है:

call stack in second callbacl

यह पता चलता है कि दूसरा कॉलबैक 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 
+0

डिस्पैचर। इन्वोक यूआई थ्रेड को आमंत्रित करने का एक तरीका है, जो मैं टालना चाहता हूं। – jan

+0

आप सही हैं - वह बकवास था (बक्षीस के लिए बहुत भूखा)। मैंने उत्तर के साथ उत्तर अपडेट किया है - उम्मीद है - अधिक उपयोगी विश्लेषण। – dlatikay

+0

ग्रेट फाइंड। थ्रेड चेक ने मेरे लिए इसे हल किया। मैंने डिस्पैचर चाल के बजाय अपार्टमेंट स्थिति की जांच की क्योंकि यह एक वीएसटीओ है। गहरे गोता के लिए धन्यवाद – jan

संबंधित मुद्दे