Почему функция получения дескрипторов окон процессов не работает стабильно в Delphi: поиск и устранение причин
При работе с функциями Windows API в Delphi может возникнуть проблема, когда функция, предназначенная для получения дескрипторов окон процессов, работает нестабильно. В частности, функция EnumWindows может иногда вызываться только один раз, не собирая полный список окон. В этом материале мы рассмотрим, почему это происходит и как исправить данную проблему.
Описание проблемы
Разработчик столкнулся с проблемой при попытке получить дескрипторы окон, принадлежащих конкретному процессу. Функция GetWindowHandlesByPID использует EnumWindowsProcMy для перебора окон, однако в некоторых случаях EnumWindowsProcMy вызывается всего один раз, и список окон остается пустым.
Анализ кода
В коде функции GetWindowHandlesByPID используется вложенная функция EnumWindowsProcMy для обработки каждого окна. В этой функции происходит проверка, принадлежит ли текущее окно процессу с указанным идентификатором. Если да, то дескриптор окна добавляется в список.
function GetWindowHandlesByPID(const cPID: Cardinal): TStringList;
var
slHandles: TStringList;
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;
Result := TRUE; // Здесь была ошибка - Result не инициализирован
end;
begin
// ...
EnumWindows(@EnumWindowsProcMy, LPARAM(cPID));
// ...
end;
Подтвержденный ответ
Проблема заключается в том, что в функции EnumWindowsProcMy не устанавливается значение Result. В соответствии с требованиями API, функция обратного вызова должна возвращать TRUE для продолжения перебора окон или FALSE для его прекращения. В данном случае Result инициализируется случайным значением, что может привести к непредсказуемому поведению.
Кроме того, использование вложенной функции для EnumWindows некорректно, так как вложенные функции в Delphi не могут быть использованы в качестве обратных вызовов для Win32 API. Вместо этого следует использовать отдельную функцию.
Исправление кода
Для корректной работы функции GetWindowHandlesByPID необходимо использовать отдельную функцию обратного вызова, которая возвращает TRUE, и инициализировать список окон за пределами этой функции. Пример исправленного кода:
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; // Теперь функция корректно возвращает 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.
Или альтернативный вариант с передачей списка в функцию:
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.
Заключение
При работе с функциями Windows API важно следовать правилам, установленным для обратных вызовов. В данном случае, использование вложенной функции привело к ошибке, которая была исправлена путем создания отдельной функции обратного вызова и корректного возврата значения Result.
Функция `EnumWindows` в Delphi работает нестабильно при получении дескрипторов окон процессов из-за неправильной инициализации значения `Result` в обратном вызове, что приводит к неожиданному завершению перебора окон.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.