標籤:
UIAutomation是.Net 3.5之後提供的“介面自動化測試”技術,本來是給測試人員用的,不過UIAutomation由於也是介面自動操作的技術,比直接使用keybd_event、GetWindowText等Win32的API進行介面類比操作簡單很多,因此也可以用UIAutomation做軟體的“外掛”。
我手頭正好有這樣一個需求,如鵬網有一個內部使用的一個工具(購買的第三方軟體),用於根據學生的機器碼計算“播放密碼”,這個工具只提供了圖形化的介面:
輸入機器碼之後點擊【建立播放密碼】按鈕就能產生播放密碼。
如鵬網第二期學習輔助系統的開發中需要開發“自動產生播放密碼”的功能,也就是學生在瀏覽器中輸入他的機器碼,網站自動計算他的播放密碼。
由於這個工具只提供了圖形化的介面,沒有提供API,所以我就想到使用類比點擊的方法來進行“自動化”,直接使用Win32太麻煩,AutoIt使用還要註冊組件,因此就想到了UIAutomation。
完成的效果如下:
下面分享一下主要技術。
學習UIAutomation之前一定要知道,Windows中的程式介面元素都是由“視窗組成的”(DirectUI等除外),按鈕、文字框等都是視窗,視窗之間也有父子關係。Windows案頭是所有視窗的根視窗。
UIAutomation支援普通Win32程式(不是VC++、.Net開發的也支援,因為本質上都是Win32程式)和WPF程式,但是不支援普通的DirectUI視窗(比如QQ、瀏覽器)。
使用UIAutomation之前先要添加對UIAutomationClient、 UIAutomationProvider、 UIAutomationTypes三個程式集的引用。所有的介面元素都是由AutomationElement組成,每個視窗就是一個AutomationElement,因此AutomationElement之前也有父子結構。
可以使用AutomationElement.RootElement獲得案頭的根項目;使用AutomationElement.FromHandle(IntPtr hwnd)從Win32視窗控制代碼拿到AutomationElement對象。
拿到一個AutomationElement通常要遍曆他的子項目。遍曆子項目之前需要先瞭解“遍曆條件”的概念,遍曆條件就是按照什麼樣的條件去搜尋子項目。所有的條件都繼承自Condition類,Condition類的主要子類有PropertyCondition、AndCondition 、NotCondition 、OrCondition,這些之類之間可以進行複合的組合,形成各種複雜的遍曆條件。
PropertyCondition是根據屬性的名字和值進行過濾的。它建構函式的第一個參數為屬性的名字,所有支援的屬性都在AutomationElement的***Property這些靜態成員中;建構函式的第二個參數為被比較的值。又可以使用AndCondition、NotCondition、OrCondition把各個條件進行複雜的邏輯組合。比如下面的conditionBtn9就是“類名為Button並且名字為9”的條件:
Condition conditionBtn9 = new AndCondition( new PropertyCondition(AutomationElement.ClassNameProperty, "Button"), new PropertyCondition(AutomationElement.NameProperty, "9") );
Condition類有兩個固定的值,Condition. TrueCondition代表永遠為True的條件,Condition. FalseCondition代表永遠為False的條件(應該很少用)
我們可以使用AutomationElement的FindAll或者FindFirst方法進行元素的遍曆。FindAll是擷取所有符合遍曆條件的AutomationElement,因此是返回AutomationElementCollection集合,而FindFirst是返回第一個符合遍曆條件的AutomationElement,因此是返回AutomationElement。
FindFirst、FindAll的第一個參數代表搜尋的範圍,最常用的就是TreeScope.Children和TreeScope.Descendants,TreeScope.Children代表在直接子節點中搜尋,而TreeScope.Descendants代表遞迴的在所有子孫節點中搜尋。FindFirst、FindAll的第二個參數代表搜尋條件。
定位到要操作的AutomationElement之後,可以進行類比點擊(比如按鈕)或者讀寫值(比如輸入框)。比如下面的代碼中element指向的是一個按鈕,下面的代碼就是類比點擊這個按鈕:
var clickPattern = (InvokePattern)element.GetCurrentPattern(InvokePattern.Pattern);clickPattern.Invoke();
比如下面的代碼中element指向的是一個文字框,下面的代碼就是使用字串填充這個輸入框:
ValuePattern valuePattern = (ValuePattern)element.GetCurrentPattern(ValuePattern.Pattern);valuePattern.SetValue(“如鵬網”);
下面是我實現的一個類比點擊計算機計算兩個數的乘法的數:
AutomationElement desktop = AutomationElement.RootElement;var calcFrame1 = desktop.FindFirst(TreeScope.Children,new PropertyCondition(AutomationElement.ClassNameProperty, "CalcFrame"));ClickCalcButton(calcFrame1, "3");ClickCalcButton(calcFrame1, "6");ClickCalcButton(calcFrame1, "5");ClickCalcButton(calcFrame1, "*");ClickCalcButton(calcFrame1, "1");ClickCalcButton(calcFrame1, "2");ClickCalcButton(calcFrame1, "=");
其中ClickCalcButton是我封裝的一個方法:
private static void InvokeButton(AutomationElement e){ InvokePattern invoke = (InvokePattern)e.GetCurrentPattern(InvokePattern.Pattern); invoke.Invoke();}private static void ClickCalcButton(AutomationElement calcFrame1, string name){ Condition conditionBtnPlus = new AndCondition( new PropertyCondition(AutomationElement.ClassNameProperty, "Button"), new PropertyCondition(AutomationElement.NameProperty, name) ); var btn = calcFrame1.FindFirst(TreeScope.Descendants, conditionBtnPlus); if (btn == null) {throw new Exception("找不到名字為"+name+"的計算機按鈕"); } InvokeButton(btn);}
文章篇幅有限,特別是對於一些沒有Win32基礎的朋友,光看上面的文字會不太容易懂,因此我錄製了一套大約90分的視頻教程,從入門到實用的講解了UIAutomation的使用,感興趣的朋友可以看。
視頻教程地址如下 http://www.rupeng.com/Courses/Chapter/298
【視頻教程】使用UIAutomation開發軟體外掛