使用 Window Handle 撰寫自動化程式

November 17, 2019

在 Windows 系統上,可以透過操作視窗元件的 Handle ,自動的執行一系列的步驟,且不會像錄製滑鼠 / 鍵盤動作的方式,會受到當時畫面的干擾。

關鍵字

  • hWnd:常見的 Handle 變數名稱,h 代表 Handle,Wnd 表示 Window,意思為 Handle of Windows。

參考:Definition of Window Handle

用 C# 操作 Handle

1. 使用 Spy++ 或其它工具,找到特定視窗和元件的 Class 和 Caption。

2. 使用 FindWindow API 找到視窗的 Handle,這些 Handle 其實為視窗元件在記憶體中的指標,因此會以 IntPtr 的方式呈現。FindWindow() 可以用於找視窗的 Handle,FindWindowEx() 則可以根據找到的 Handle 尋找子元件的 Handle。

3. FindWindow() / FindWindowEx() 皆可用於找特定 Class 或(和)特定 Caption (Title) 的視窗。

4. 可使用 SendMessage() 輸入內容至編輯器內。如果要輸入按鍵訊號 (如鍵盤快捷鍵,Ctrl+C 等組合鍵) 的話,應使用 PostMessage()。以下程式碼參考自:C# Win32 API FindWindow/ FindWindowEx/SendMessage – Neil(After Work)

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
 
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr hwndParent,IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
 
[DllImport("user32.dll", SetLastError = true)]
static extern int SendMessage(IntPtr hWnd, int msg, int wParam, StringBuilder lParam);
 
const int WM_SETTEXT = 0x000C;
 
static void Main(string[] args)
{
IntPtr MainWnd = FindWindow("Notepad", null);
IntPtr ChildWnd = FindWindowEx(MainWnd, IntPtr.Zero, "Edit", "");
StringBuilder InsStr = new StringBuilder();
InsStr.Append("ABCDEFGHIJK565465465");
SendMessage(ChildWnd, WM_SETTEXT, 0, InsStr);
}

5. 要送出按鍵訊號,可透過 PostMessage(),按鍵代碼可參考 Microsoft Docs 提供的特殊按鍵列表 。以下程式碼參考自:handle - SendMessage Return Key and Text to WindowHandle in C# - Stack Overflow

[DllImport("User32.Dll")]
public static extern Int32 PostMessage(IntPtr hWnd, int msg, int wParam, int lParam);

const int WM_KEYDOWN = 0x0100;
const int VK_TAB = 0x09;

PostMessage(process.MainWindowHandle, WM_KEYDOWN, VK_TAB, 1);

6. 特殊按鍵列表:Virtual-Key Codes - Win32 apps - Microsoft Docs

7. 若 Spy++ 的 Class 內容顯示如 “#32770 (Dialog)” 的編號內容,可以直接輸入”#32770”即可。參考自:Findwindow does NOT find “#32770 (Dialog)” class window

8. 同樣使用 PostMessage() 送出滑鼠按鈕的訊號,座標的位置使用位元運算設定:

int x=58;
int y=32;

PostMessage(this.Handle, WM_LBUTTONDOWN, 0, (x & 0xFFFF) + (y & 0xFFFF) * 0x10000);
PostMessage(this.Handle, WM_LBUTTONUP, 0, (x & 0xFFFF) + (y & 0xFFFF) * 0x10000);

PostMessage(this.Handle, WM_LBUTTONDOWN, 0, x + (y<<16));
PostMessage(this.Handle, WM_LBUTTONUP, 0, x+(y<<16));

程式碼參考自:c# 调用win32模拟点击的两种方法 - li-peng - 博客园

其它參考資料