Glory to Russia
Glory to Russia

Reputation: 18730

How to find all text fields in a window?

I have a window with two text fields inside it.

How can I get the handles of both text fields using WinAPI calls?

Note: Both text boxes belong to a different application (I make the WinAPI calls in application A and the text boxes are located in application B).

Update 1:

I get Invalid window handle message when invoking GetClassName.

I suppose that something is wrong with my declaration of the callback function.

EnumChildWindows is invoked from one of the methods of TMyClass like this:

EnumChildWindows(handle, @TMyClass.CBList, 0);

Here's the code of the callback function.

function TMyClass.CBList(Win: THandle; lp: LPARAM): Boolean; stdcall;
var
  ClassName:array [1..1024] of Char;
begin
  GetClassName(Win, PChar(@ClassName), 1024);
  OutputDebugString(PChar('SysErrorMessage(GetLastError): '));

  result := true;
end;

Upvotes: 1

Views: 4041

Answers (1)

David Heffernan
David Heffernan

Reputation: 613382

  1. Use FindWindow or EnumWindows to find the top level window of the target application.
  2. Call EnumChildWindows to enumerate all children of the top level window.
  3. In the enumeration callback use GetClassName to check for the desired window class. It would be EDIT for a raw Win32 edit window, for example.

Use a tool like Spy++ to understand the structure of the target app, and find out the precise window class names that it uses.


Your questions in the comments about how to call GetClassName got me thinking. If you are using XE3, you could write a simple type record helper for HWND to make it syntactically cleaner to get hold of the class name:

type
  THWNDHelper = record helper for HWND
  private
    function GetClassName: string;
  public
    property ClassName: string read GetClassName;
  end;

function THWNDHelper.GetClassName: string;
var
  Buffer: array [0..255] of Char;
begin
  if Winapi.Windows.GetClassName(Self, @Buffer, Length(Buffer))=0 then
    RaiseLastOSError;
  Result := Buffer;
end;

And then you can write hwnd.ClassName to obtain the window class name. Of course, if you are not using XE3 you can do it like this:

function GetWindowClassName(hwnd: HWND): string;
var
  Buffer: array [0..255] of Char;
begin
  if GetClassName(hwnd, @Buffer, Length(Buffer))=0 then
    RaiseLastOSError;
  Result := Buffer;
end;

Note that I am using a buffer length of 256 since window class name lengths are limited to be no longer than that.


Regarding the code in the update, you must not use an instance method for the callback. The callback must be declared like this:

function EnumChildWindowsCallback(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
begin
  OutputDebugString(PChar(GetWindowClassName(hwnd)));
  Result := True;
end;

This is made clear in the documentation. Unfortunately the declaration of EnumChildWindows in Windows.pas completely abandons type safety of the callback function. So you have to get it right without help from the compiler.

Note also that HWND and THandle are not the same thing. Don't mix them up.

Upvotes: 5

Related Questions