天天看點

自動化快速入門:發現浏覽器視窗,自動執行 JavaScript

作者:aardio

使用 Windows 自帶的 MSAA 發現浏覽器視窗,自動執行 JavaScript 很簡單!

aardio 代碼示例:

import winex;
import winex.accObject;
import winex.key;

//周遊浏覽器視窗(相容 Chrome,Edge 等)
for hwnd,title in winex.each("Chrome_WidgetWin_1") { 
	
	//擷取 MSAA 接口對象
	var accObject = winex.accObject.fromWindow(hwnd);	
	
	//查找文本框
	var edit = accObject.find(
		role = 0x2A;
		name = "<Address and search bar>|<位址和搜尋欄>";
	)
	
	if(edit){ 
		//擷取浏覽器位址欄内容
		var url = edit.value();
		
		//修改浏覽器位址欄内容
		edit.setValue("javascript:alert(document.location.href)")
		edit.takeFocus();

		//背景發送按鍵消息
		winex.key.click(hwnd,"ENTER");
		thread.delay(1000);
	} 
}           

運作一下看看效果:

自動化快速入門:發現浏覽器視窗,自動執行 JavaScript

所有 Windows 系統都自帶 MSAA,接口簡單,易于使用,生成的 EXE 程式體積也會很小。aardio 标準庫 winex.accObject 則對 MSAA 做了進一步封裝,用法就更簡單了。

視窗基礎知識

1、什麼是視窗

「視窗」是應用程式在螢幕上建立的一個顯示區域,通常用于接收并處理使用者操作,并顯示要輸出的内容。視窗上的文本框、按鈕、菜單這些也都是視窗。

我們一般将頂層獨立視窗稱為「窗體」,而窗體上的子視窗稱為「控件」。

2、什麼是視窗句柄

視窗句柄是一個用于唯一标準視窗的整數值。

其實很多系統資源,例如位圖、程序、線程都有唯一标準資源的句柄。

在 aardio 中所有句柄都存為指針類型,唯有視窗句柄是普通的數值類型。

3、無句柄視窗

無句柄視窗是指該視窗上的控件沒有建立子視窗,典型的例如網頁上的按鈕、文本框都沒有視窗句柄。MSAA 可用于操作無句柄視窗。

使用視窗探測器

請在 aardio 中打開 『工具 > 探測器 > 視窗探測器』:

自動化快速入門:發現浏覽器視窗,自動執行 JavaScript

拖動『視窗探測器』左下角的十字圖示到目标視窗上,就會顯示視窗資訊。

使用視窗探測器我們可以發現 Chrome, Edge 等浏覽器的網頁視窗類名都是 "Chrome_WidgetWin_1", 是以我們可以用下面的 aadio 代碼擷取所有打開的浏覽器視窗:

import winex;

for hwnd,title in winex.each("Chrome_WidgetWin_1") { 
	
	
}           

aardio 中 winex 名字空間的所有庫、函數都是用于操作外部程式視窗的。

winex.each() 用于周遊所有符合條件的視窗, winex.each() 的第一個參數可以指定視窗類名,這個類名支援模式比對文法( 類正規表達式,但更簡單,用法請參考文法文檔 )。

自視窗句柄擷取 MSAA 對象

自視窗句柄擷取 MSAA 對象,代碼很簡單:

import winex;
import winex.accObject;

//周遊浏覽器視窗(相容 Chrome,Edge 等)
for hwnd,title in winex.each("Chrome_WidgetWin_1") { 
	
	//擷取 MSAA 接口對象
	var accObject = winex.accObject.fromWindow(hwnd);	

}           

使用 MSAA 探測工具

請在 aardio 中打開 winex.accObject 的文檔或源碼,搜尋“ACC對象浏覽工具” 并下載下傳該工具( inspect.exe )。

運作 inspect.exe ,點選下圖的『 Watch Cursor 』圖示:

自動化快速入門:發現浏覽器視窗,自動執行 JavaScript

也就是允許探測滑鼠指向的視窗。

然後将滑鼠移向浏覽器的位址欄,Inspect 找到了位址欄所在的 ACC 對象,并顯示了一堆資訊,我們重點關注這幾行:

Name:	"Address and search bar"
Role:	editable text (0x2A)           

Name 是 ACC 對象的名稱。

Role 是 ACC 對象的角色,其實就是控件類型。

根據上面的資訊,我們修改代碼擷取浏覽器位址欄:

import winex;
import winex.accObject;
import console;

//周遊浏覽器視窗(相容 Chrome,Edge 等)
for hwnd,title in winex.each("Chrome_WidgetWin_1") { 
	
	//擷取 MSAA 接口對象
	var accObject = winex.accObject.fromWindow(hwnd);	
	
	//查找位址欄
	var edit = accObject.find(
		role = 0x2A;
		name = "<Address and search bar>|<位址和搜尋欄>";
	)
	
	//顯示位址欄的内容
	if(edit) console.log( edit.value() )

}

console.pause();           

在 aardio 中運作上面的代碼,我們幹淨利索地拿到了浏覽器位址欄的網址。

拿到一個 accObject 對象以後,可以調用 accObject.find() 函數繼續查找符合條件的子節點。而查找參數就是我們用 Inspect.exe 探測到的參數。

查找參數中,role, state 可以是文本,也可以是數值,一般建議用數值( 速度更快 )。

上面的 name 參數用到了模式比對:

name = "<Address and search bar>|<位址和搜尋欄>";           

這個模式表達式中的 | 線是 “或” 的意思,而 < > 括号用于包含子串。如果目标 ACC 對象的 name 包含 "Address and search bar" 或者 "位址和搜尋欄" 都符合條件。

背景按鍵

aardio 标準庫 key,mouse 用于對前台視窗模拟按鍵滑鼠。

例如:

key.press("ENTER")           

作用就是模拟按下Enter鍵。

如果我們改用 winex.key, winex.mouse 就可以直接向背景視窗發送按鍵或滑鼠消息。這樣的好處是不會幹擾使用者操作。

例如向浏覽器視窗發送Enter鍵消息:

import winex;
import winex.key;

//周遊浏覽器視窗(相容 Chrome,Edge 等)
for hwnd,title in winex.each("Chrome_WidgetWin_1") { 

	//背景發送按鍵消息
	winex.key.click(hwnd,"ENTER");
	thread.delay(1000);
}           

鍵名

操作按鍵的函數都需要用到鍵名。

我們還可以直接運作 aardio 『 工具 > 滑鼠按鍵 > 按鍵指令生成器』

自動化快速入門:發現浏覽器視窗,自動執行 JavaScript

在『按鍵指令生成器』視窗内我們任意按鍵,就可以顯示對應的鍵名了。

聽說 UIA 更先進

有時候先進也是一種負擔,飛機比自行車先進,這不等于任何時候都要用飛機替代自行車。

當然,在 aardio 中調用 UIA 也是很簡單的,示例( 可獨立運作 ):

import process;
process.executeWaitInput("notepad.exe",io.getSpecial(0x25/*_CSIDL_SYSTEM*/,"drivers\etc\HOSTS"));
 
//導入 .Net 類
import System.Windows.Automation;
TreeScope = ::UIAutomationTypes.import("System.Windows.Automation.TreeScope"); 

//通路 .Net 類的靜态成員
Automation = System.Windows.Automation;
AutomationElement = Automation.AutomationElement;
RootElement = AutomationElement.RootElement;

//查找記事本視窗
var condNotepadClass = Automation.PropertyCondition(AutomationElement.ClassNameProperty,"Notepad")
var notepad = RootElement.FindFirst( TreeScope.Children, condNotepadClass) 

//查找記事本的編輯框
var condEditClass = Automation.PropertyCondition(AutomationElement.ClassNameProperty,"Edit");
var editBox = notepad.FindFirst( TreeScope.Descendants, condEditClass); 

if(!editBox){
	//Windows 11
	condEditClass = Automation.PropertyCondition(AutomationElement.ClassNameProperty,"RichEditD2DPT");
	editBox = notepad.FindFirst( TreeScope.Descendants, condEditClass); 	
}

//擷取記事本内的文本
var textPattern =  editBox.GetCurrentPattern(Automation.TextPattern.Pattern);
var text = textPattern.DocumentRange.GetText(50)

import win.dlg.message;
win.dlg.message().info(text + " ……")           

然并卵,WebDriver 不香麼?

aardio 調用 WebDriver 就更簡單了,示例:

import chrome.driver;

var driver = chrome.driver(); 

//建立會話,打開chrome浏覽器,調用參數僅用于示範( 可以省略 )。
var browser = driver.startBrowser();

//打開網頁
browser.go("網址你猜")

//查找文本輸入框,傳回的DOM對象也可以使用ququerySelector繼續查找子節點
var ele = browser.querySelector("body").querySelector("#kw"); 

//在網頁輸入框輸入内容
ele.setValue( "ChromeDriver" )           

繼續閱讀