Lin
Lin

Reputation: 13

EnumWindows not working sometimes

I'm creating a function to get all window handles with processid.

My code:

unit untCommonUitls;

interface

uses
   System.SysUtils, Winapi.Windows, Winapi.Messages, System.Classes;



function GetWindowHandlesByPID(const cPID: Cardinal): TStringList;

var
  slHandles: TStringList;

implementation

function GetWindowHandlesByPID(const cPID: Cardinal): TStringList;
var
  hFound: THandle;

  function EnumWindowsProcMy(_hwnd: HWND; ProcessId: Cardinal): BOOL; stdcall;
  var
    dwPid: Cardinal;
  begin
    GetWindowThreadProcessId(_hwnd, @dwPid);
    if ProcessId = dwPid then
    begin
      hFound := _hwnd;
      slHandles.Add(IntToStr(hFound));
    end;
  end;

begin
  if not Assigned(slHandles) then
  begin
    slHandles := TStringList.Create;
  end;
  slHandles.Clear;
  EnumWindows(@EnumWindowsProcMy, LPARAM(cPID));
  Result := slHandles;
end;

When I try this code, everything works, EnumWindowsProcMy call many times, slHandles get a list of handle;

But in others, it does not work. EnumWindowsProcMy only call once, so slHandles is empty.

Upvotes: 1

Views: 1224

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 598134

You are not setting the Result of EnumWindowsProcMy() to anything, so it contains a random value when EnumWindowsProcMy() exits. You MUST return either TRUE to continue enumeration or FALSE to stop it.

But, more importantly, you CANNOT use a nested function for the EnumWindows() callback (or any other Win32 callback, for that matter)! A nested function shares the parent function's stack frame, so they can share parameters and local variables with each other. As such, that involves compiler trickery that makes a nested function incompatible for use with the Win32 API. The callback MUST be a standalone function!

Use something more like this instead:

unit untCommonUitls;

interface

uses
   System.Classes;

function GetWindowHandlesByPID(const cPID: Cardinal): TStringList;

implementation

uses
  Winapi.Windows;

var
  slHandles: TStringList = nil;

function MyEnumWindowsProc(wnd: HWND; ProcessId: LPARAM): BOOL; stdcall;
var
  dwPid: Cardinal;
begin
  GetWindowThreadProcessId(wnd, @dwPid);
  if dwPid = ProcessId then
    slHandles.Add(IntToStr(wnd));
  Result := TRUE;
end;

function GetWindowHandlesByPID(const cPID: Cardinal): TStringList;
begin
  if not Assigned(slHandles) then
    slHandles := TStringList.Create
  else
    slHandles.Clear;
  EnumWindows(@MyEnumWindowsProc, cPID);
  Result := slHandles;
end;

finalization
  slHandles.Free;

end.

Alternatively:

unit untCommonUitls;

interface

uses
   System.Classes;

procedure GetWindowHandlesByPID(const cPID: Cardinal; List: TStrings);

implementation

uses
  Winapi.Windows;

type
  PMyEnumInfo = ^MyEnumInfo;
  MyEnumInfo = record
    ProcessId: DWORD;
    List: TStrings;
  end;

function MyEnumWindowsProc(wnd: HWND; param: LPARAM): BOOL; stdcall;
var
  dwPid: Cardinal;
begin
  GetWindowThreadProcessId(wnd, @dwPid);
  if dwPid = PMyEnumInfo(param).ProcessId then
    PMyEnumInfo(param).List.Add(IntToStr(wnd));
  Result := TRUE;
end;

procedure GetWindowHandlesByPID(const cPID: Cardinal; List: TStrings);
var
  Info: MyEnumInfo;
begin
  Info.ProcessId := cPID;
  Info.List := List;
  List.BeginUpdate;
  try
    EnumWindows(@MyEnumWindowsProc, LPARAM(@Info));
  finally
    List.EndUpdate;
  end;
end;

end.

Upvotes: 8

Related Questions