目錄

廣告 AD
廣告被擋住了 QAQ

不想慢慢用滑鼠點?用程式操控 BlueStacks 的方法!

BlueStacks 要用電腦來操控到底要怎麼操控?

難道只能用人工一個一個點嗎?

今天就來揭曉吧 ~

廣告 AD
廣告被擋住了 QAQ

以下提供了兩種方法來用除了人工點擊的方式來操控 BlueStacks,既然 BlueStacks 是在 Windows 上運行的,那就會有用 Windows 的方法 (Win32 API),另外,BlueStacks 是模擬 Android,因此也會有 Android 的方法 (ADB)。

我們可以透過 Win32 API 來跟 Windows 互動,使用 Win32 API,我們可以發送訊息給視窗,告訴視窗:有個按下的訊號在位置 x=100, y=200 的地方。

但是 Windows 上你會開啟很多視窗,可能有瀏覽器、記事本…等,所以我們要指定我們的目標視窗,在 Windows 上,每個視窗都有一個 ID,我們稱作「視窗控制碼」 (HWND)。

我們可以依照視窗的名稱來找到我們要的視窗的 HWND,以下我們使用 Python 來操作,首先來安裝需要的套件 pywin32

bash

pip install pywin32

安裝完後,我們可以列舉出所有當前 Windows 所有視窗的 HWND,並使用 HWND 取得每個視窗的名稱,判斷是否是我們要的 HWND。我們這次的目標是找到 BlueStacks 的視窗,但因為 BlueStacks 的觸控視窗是在子視窗內,因此我們要額外拿到子視窗的 HWND,直接找到的 BlueStacks 視窗其實是這個:


BlueStacks 的控制列

python

import win32con
import win32api
import win32gui

window_name = 'BlueStacks App'
target_hwnd = None

def enumHandler(hwnd, _):
  # 使用 HWND 得到當前視窗的名稱
  windowText = win32gui.GetWindowText(hwnd)
  # 判斷是否是我們的目標視窗
  if window_name in windowText:
    # 取得子視窗
    hwndChild = win32gui.GetWindow(hwnd, win32con.GW_CHILD)
    global target_hwnd
    target_hwnd = hwndChild

# 列舉出所有的視窗的 HWND,並呼叫 enumHandler 來處理
win32gui.EnumWindows(enumHandler, None)

有了 HWND 之後我們就可以發送訊息啦 ~ 注意:座標原點 (0,0) 是目標畫面的左上角,因此計算的時候要小心不要搞錯了。
注意
使用這方法時,目標頁面一定是"焦點",也就是要在可以操控的狀態下,最小化後、被遮擋住的畫面或是不是焦點的頁面,你傳送任何訊息都是沒有反應的。

滑鼠點擊其實是兩個動作:「按下去」和「放開」,因此我們要傳送這兩個兩個訊息。

事件:

  • 按下左鍵:WM_LBUTTONDOWN
  • 放開左鍵:WM_LBUTTONUP
  • 按下右鍵:WM_RBUTTONDOWN
  • 放開右鍵:WM_RBUTTONUP
  • 按下中鍵:WM_MBUTTONDOWN
  • 放開中鍵:WM_MBUTTONUP

按鍵:

  • 左鍵:MK_LBUTTON
  • 右鍵:MK_RBUTTON
  • 中鍵:MK_MBUTTON

python

def tap(hwnd:int, pos: Tuple[int, int]):
  # 滑鼠點擊座標
  lParam = win32api.MAKELONG(*pos)
  # 按下左鍵
  win32api.PostMessage(hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, lParam)
  # 放開左鍵
  win32api.PostMessage(hwnd, win32con.WM_LBUTTONUP, None, lParam)

# 點擊 (100,100) 的位置
tap(target_hwnd, 100, 100)

滑動的部分比較複雜一點,包含「按下」、「移動」和「放開」,與點擊不同,多了移動的部分,我們需要計算出游標在移動過程中的位置,並傳送給目標視窗移動的座標,這個傳送的次數越多,代表移動的越慢,反之則是移動的越快。

python

def drag_press(hwnd:int, src: Tuple[int, int], dst: Tuple[int, int]):
  clickPos = win32api.MAKELONG(src[0], src[1])
  # 按下左鍵
  win32api.PostMessage(hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, clickPos)

  # 依照距離計算要回傳的次數 (max_value)
  offset = (dst[0] - src[0], dst[1] - src[1])
  max_value = max(abs(offset[0]), abs(offset[1]))
  max_value = max_value//5 if max_value > 60 else max_value//2
  # 計算每步的位移距離
  step = (offset[0]/max_value, offset[1]/max_value)

  # 傳送移動中的座標
  for i in range(max_value):
    clickPos = win32api.MAKELONG(src[0]+int(step[0]*i), src[1]+int(step[1]*i))
    # 滑鼠移動
    win32api.PostMessage(hwnd, win32con.WM_MOUSEMOVE, win32con.MK_LBUTTON, clickPos)
    # 設定延遲
    time.sleep(0.001)

def drag_up(hwnd:int, dst: Tuple[int, int]):
  clickPos = win32api.MAKELONG(dst[0], dst[1])
  # 放開左鍵
  win32api.PostMessage(hwnd, win32con.WM_LBUTTONUP, win32con.MK_LBUTTON, clickPos)

def drag(hwnd:int, src: Tuple[int, int], dst: Tuple[int, int]):
  # 按下開始滑動
  drag_press(hwnd, src, dst)
  # 抬起
  drag_up(hwnd, dst)

# 從位置 (100,100) 滑動到位置 (200,200)
drag(target_hwnd, (100, 100), (200, 200))

ADB (Google 官網),全名 Android Debug Bridge,是一個工具讓我們可以跟 Android 手機溝通,是開發人員在開發的一個好幫手,剛好 BlueStacks 有提供 ADB,我們可以使用 ADB 讓我們可以和一般手機一樣直接操控 BlueStacks。


首先要先安裝 ADB,如果你想要用 CMD 來操控的話可以直接去網站下載 ADB,連結

下載 ADB 的執行檔

但今天我想用 Python 來操控,除了用 subprocess 來呼叫 exe 之外,我們可以使用 adbutils (openatx/adbutils) 來連接,adbutils 是一個純 Python 的套件,可以幫助我們在 Python 上和 Android 裝置溝通,可以透過以下指令安裝。

bash

pip3 install adbutils

再來我們要找到 ip 跟 port 來連接 ADB,因為 BlueStacks 要使用 Wifi 的方式來連接,BlueStacks 的會顯示在設定裡面,或是想要用 Python 來直接獲取 ADB 的 port,可以參考我之前的文章,如下。

BlueStacks 在 Hyper-V 模式下取得 ADB Port 的方法

BlueStacks 在 Hyper-V 模式下取得 ADB Port 的方法

之前用 BlueStacks 模擬器和 ADB 來測試程式,但我為了使用 WSL,開啟了 Hyper-V,這導致了 BlueStacks 的 ADB 的 Port 會一直更改,並不是固定的,每次重新開啟 BlueStacks 模擬器後,都要更改設定...

閱讀全文

BlueStacks ADB 的 IP 和 Port

接著使用 ADB 連上裝置,有分 CMD 的版本和 Python 套件的版本。

  • CMD:
    這方法只支援 Android 10 或以下的版本,新版本需要到官網查看。

    bash

    # adb connect <ip>:<port>
    adb connect 127.0.0.1:5555
  • Python: 連接的時候可以設定時間,超過時間未連上可以做其他處理。

    python

    from adbutils import adb, AdbTimeout
    try:
      # 連到 ip = 127.0.0.1 和 port = 5555,時間設定為 2 秒
      adb.connect("127.0.0.1:5555", timeout=2.0)
    except AdbTimeout as e:
      print(e)
  • CMD:
    可能會連上很多台裝置,為了直接指定裝置是哪一台,我們可以加上 -s <ip>:<port> 來指定目標的裝置,如果只有一台連上,那可以不用指定。x 和 y 分別代表點擊的座標。

    bash

    # adb -s <ip>:<port> shell input tap <x> <y>
    adb -s 127.0.0.1:5555 shell input tap 100 100
  • Python:
    一樣為了處理多台連接裝置的問題,我們可以指定裝置,如果只有一台,可以不用傳入參數。

    python

    d = adb.device("127.0.0.1:5555")
    # d = adb.device()
    
    # 點擊座標 (100,100)
    d.click(100, 100)
  • CMD:

    bash

    # adb -s <ip>:<port> shell input swipe <x1> <y1> <x2> <y2> [ms]
    adb -s 127.0.0.1:5555 shell input swipe 100 100 200 200 250
  • Python:

    python

    d = adb.device("127.0.0.1:5555")
    # d = adb.device()
    
    # 從座標 (100,100) 滑動到座標 (200,200) 花費 0.5 秒
    d.swipe(100, 100, 200, 200, 0.5)

這邊介紹兩種方法來操控 BlueStacks,兩種方法都有優缺點,依我們目前的使用來說,ADB 是最好的選擇。

  • Win32 API
    • 優點:
      • 模擬滑鼠點擊,較不易被偵測到腳本
      • 可以用在其他任何的視窗上
    • 缺點:
      • 一定要 focus 在這個視窗,否則無法發送訊號
  • ADB
    • 優點:
      • 有其他功能可以使用,像是偵測當前 APP,崩潰後可以重啟
      • 視窗不必 focus
    • 缺點:
      • 只能用在 Android 系統上

廣告 AD
廣告被擋住了 QAQ
留言
  • 最新
  • 最早
  • 熱門
Powered by Waline v3.5.1