【问题标题】:How to get handles of different controls with the same class name ?如何获取具有相同类名的不同控件的句柄?
【发布时间】:2012-05-26 04:13:06
【问题描述】:

我正在尝试使用 delphi 编程访问第三方应用程序的“文本框”,因此我需要使用 FindWindowEx(...) 函数找到每个“文本框”的句柄。

问题是,由于所有文本框都具有相同的类名和“无窗口名称”,这个函数只能给我第一个 TextBOx 句柄!

如何在没有名称的情况下获取其余的文本框句柄?

提前致谢。

【问题讨论】:

  • 我认为您对 FindWindowEx(PARENT_WINDOW_HANDLE, GW_HWNDNEXT, NIL, NIL) 感兴趣
  • 谢谢!但是“GW_HWNDNEXt”给出了下一个控制句柄,它可能不是下一个文本框。
  • 是的,但是他需要构建一个递归函数来枚举所有子窗口,例如 2 个文本框可能是父窗口的子窗口,而其他文本框是另一个窗口的子窗口,所以他会需要先用find“搜索”,再找下一个……

标签: delphi delphi-xe2 delphi-xe


【解决方案1】:

您可以使用EnumChildWindows枚举第三方应用程序窗口的所有子窗口,并测试每个枚举窗口的类名是否为“文本框”类。示例:

function EnumChildren(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
const
  TextBoxClass = 'EDIT'; (?)
var
  ClassName: array[0..259] of Char;
begin
  Result := True;
  GetClassName(hwnd, ClassName, Length(ClassName));
  if ClassName = TextBoxClass then
    TStrings(lParam).Add(IntToHex(hwnd, 8));
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Clear;
  EnumChildWindows(OtherAppWnd, @EnumChildren, UINT_PTR(Memo1.Lines));
end;

【讨论】:

  • 非常感谢。对您的代码稍作改动,效果很好。
【解决方案2】:

为了有效地使用FindWindowEx(),您需要提前了解窗口的UI 结构,例如通过Spy++、Winspector 或其他类似工具。这样您就可以知道有多少控件具有相同的类类型,它们的父/子关系彼此之间的关系等,因此您可以相应地编码FindWindowEx()。或者,如果目标 UI 使用 Dialog ID(VCL 不使用,但 Microsoft 通常使用),那么您可以使用 GetDlgItem() 直接获取所需控件的句柄,而无需在代码中寻找它们(同样、Spy++ 和类似工具可以向您显示这些 ID 是什么,以便您对其进行编码)。

【讨论】:

    【解决方案3】:

    基于@Sertac Akyus 我的 Delphi 7 (2002) 32 位代码,适用于 Windows 10 64 位专业版 远程(本地桌面)应用程序: (注意:我不知道为什么,但是这里所有带有@s 的代码都不起作用!):
    | Win32API |描述| |:-------- | ----- | | GetClassName(winHandle, @s, 长度(s)+1); | 这似乎行不通! | | GetClassName(winHandle, PChar(s), Length(s)+1); | 这行得通! |

    unit Unit1;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, ComCtrls;
    
    type
      TForm1 = class(TForm)
        Button1: TButton;
        ListBox1: TListBox;
        procedure Button1Click(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
        winHandle: HWND;
        winTitle : string;
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    var
      s: String;
    
    function EnumChildren(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
    var
      ClassName: array[0..259] of Char;
    begin
      Result := True;
      GetClassName(hwnd, ClassName, Length(ClassName));
      Form1.ListBox1.Items.Add(IntToHex(hwnd,8) + ' ' + ClassName);
    end;
    
    procedure TForm1.Button1Click(Sender: TObject);
    begin
      ListBox1.Items.Clear;
    
      // ---------------------------------
      // 1. try to find main window hwnd:
      // ---------------------------------
      winHandle := FindWindow('TwinMain', nil);
      if winHandle < 1 then begin
      ListBox1.Items.Add('Error: 1.'); exit; end else
      ListBox1.Items.Add('win: 0x' + IntToHex(winHandle,8));
    
      // ---------------------------------
      // 2. try to find window title:
      // ---------------------------------
      SetLength(winTitle, GetWindowTextLength(winHandle));
      GetWindowText(winHandle, PChar(winTitle), Length(winTitle)+1);
      if Length(Trim(winTitle)) < 2 then begin
      ListBox1.Items.Add('Error: 2.'); exit; end else
      ListBox1.Items.Add('title: ' + winTitle);
    
      // ---------------------------------
      // 3. cross check: try to get class:
      // ---------------------------------
      SetLength(s, 100);
      GetClassName(winHandle, PChar(s), Length(s)+1);
      if Length(Trim(s)) < 2 then begin
      ListBox1.Items.Add('Error: 3.'); exit; end else
      ListBox1.Items.Add('class: ' + s);
    
      EnumChildWindows(winHandle, @EnumChildren, 0);
    end;
    
    (* here comes the form1.dfm file: *)
    object Form1: TForm1
      Left = 192
      Top = 125
      Width = 389
      Height = 328
      Caption = 'Form1'
      Color = clBtnFace
      Font.Charset = DEFAULT_CHARSET
      Font.Color = clWindowText
      Font.Height = -11
      Font.Name = 'MS Sans Serif'
      Font.Style = []
      OldCreateOrder = False
      PixelsPerInch = 96
      TextHeight = 13
      object Button1: TButton
        Left = 56
        Top = 48
        Width = 75
        Height = 25
        Caption = 'Button1'
        TabOrder = 0
        OnClick = Button1Click
      end
      object ListBox1: TListBox
        Left = 24
        Top = 112
        Width = 305
        Height = 145
        ItemHeight = 13
        TabOrder = 1
      end
    end
    

    希望这比其他许多关于 SO 的文章更有帮助。 乔,詹斯

    【讨论】:

    • 注意:@a modarator on SO:请检查表格样式的标记选项。我不确定,但我有不同的显示设置 - 在编写答案编辑器、标记和文本预览时似乎都可以工作。但是当我发布/发布答案时,我会得到其他风格。
    猜你喜欢
    • 2018-02-20
    • 1970-01-01
    • 1970-01-01
    • 2020-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-26
    • 1970-01-01
    相关资源
    最近更新 更多