首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

C# 使用 Spy++ 獲取控件 ID 後如何獲取窗口句柄

     編輯:程式設計綜合問答

問題場景

在 Spy++ 中獲取到目標控件的 Control ID (如 0x00002EE5),嘗試用 GetDlgItem(IntPtr.Zero, id) 獲取句柄,返回 0 (失敗)。

原因分析

  • GetDlgItem(hParent, nID) 需要 父窗口句柄 hParent,而非 IntPtr.Zero (桌面窗口)。
  • 控件 ID 在其 直接父窗口 範圍內唯一,必須先找到父窗口 (對話框/窗口句柄)。
  • Spy++ 顯示的控件屬性中包含 Parent Handle 或可通過樹狀視圖向上查找父窗口。

正確獲取流程

步驟 1:在 Spy++ 中查找父窗口句柄

  1. 定位目標控件,查看 Properties -> General -> Parent Handle (十六進制,如 0x00120ABC)。
  2. 或在 Spy++ 窗口樹中,控件節點的父節點即為父窗口,雙擊查看其 Handle。

步驟 2:C# 代碼獲取控件句柄


[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);

// 已知父窗口句柄 (從 Spy++ 獲取或通過 FindWindow 找到)
IntPtr hParent = new IntPtr(0x00120ABC); // 示例值,實際需動態獲取
int controlId = 0x2EE5; // Spy++ 顯示的 Control ID (十六進制)

IntPtr hControl = GetDlgItem(hParent, controlId);
if (hControl != IntPtr.Zero) {
    // 成功獲取控件句柄
    // 可發送消息、獲取文本等
    StringBuilder sb = new StringBuilder(256);
    GetWindowText(hControl, sb, sb.Capacity);
    Console.WriteLine($"控件文本: {sb}");
} else {
    int error = Marshal.GetLastWin32Error();
    Console.WriteLine($"獲取失敗, 錯誤碼: {error}");
    // 常見錯誤: 1400 (ERROR_INVALID_WINDOW_HANDLE) 父句柄無效
    // 1428 (ERROR_CONTROL_ID_NOT_FOUND) ID 在父窗口中不存在
}

步驟 3:動態查找父窗口 (實際項目中)

不能硬編碼父句柄,需通過窗口類名/標題查找:


[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

// 查找主窗口 (如類名 "#32770" 為標準對話框,或具體類名)
IntPtr hMainWnd = FindWindow(null, "目標窗口標題"); // 或按類名
if (hMainWnd == IntPtr.Zero) {
    // 嘗試枚舉頂層窗口匹配進程 ID 等
}

// 若控件在嵌套子窗口中,逐層 FindWindowEx
IntPtr hChild1 = FindWindowEx(hMainWnd, IntPtr.Zero, "Button", "確定"); // 類名/標題可為 null
// ... 直到找到直接父窗口 hParent

// 最後 GetDlgItem(hParent, controlId)

替代方案:UI Automation (推薦現代應用)

Win32 API 繁瑣且脆弱。UI Automation (System.Windows.Automation) 更穩健:


using System.Windows.Automation;

// 根據 AutomationId、Name、ControlType 查找
Condition cond = new AndCondition(
    new PropertyCondition(AutomationElement.AutomationIdProperty, "myButton"),
    new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button)
);
AutomationElement mainWnd = AutomationElement.RootElement.FindFirst(TreeScope.Children, 
    new PropertyCondition(AutomationElement.NameProperty, "窗口標題"));
AutomationElement btn = mainWnd.FindFirst(TreeScope.Descendants, cond);
if (btn != null) {
    // 獲取原生句柄 (若需互操作)
    object hWndObj = btn.GetCurrentPropertyValue(AutomationElement.NativeWindowHandleProperty);
    IntPtr hWnd = (IntPtr)hWndObj;
    // 或直接調用模式
    InvokePattern invoke = btn.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
    invoke?.Invoke();
}

總結

  1. Spy++ 獲取 Control ID 同時必須獲取其 Parent Handle。
  2. GetDlgItem 參數 hParent 必須是 直接父窗口句柄,不可為 Zero。
  3. 實際開發中動態查找父窗口:FindWindow -> FindWindowEx 鏈式調用 -> GetDlgItem。
  4. 新項目優先考慮 UI Automation,不依賴 Control ID/句柄,抗界面變更能力強。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved