Фиксация нажатий клавиш для всплывающих меню в Delphi: решение ошибки доступа через SetWindowsHookEx
Вопрос пользователя связан с необходимостью отслеживания нажатия клавиши F11 во время открытия всплывающего меню компонента TrayIcon в Delphi и последующего завершения программы. Пользователь не желает использовать функцию RegisterHotKey и планирует перехватить сообщения клавиатуры для окна, обрабатывающего всплывающее меню. Однако при попытке реализации этого плана возникла ошибка доступа.
Описание проблемы и контекст
В документации указано, что свойство PopupList.Window предоставляет доступ к дескриптору окна скрытого окна, которое обрабатывает сообщения всплывающего меню. Пользователь пытается перехватить сообщения клавиатуры, направляемые в это окно, но при этом возникает ошибка доступа с указанием адреса, с которым происходит запись.
Пример кода, вызвавшего ошибку
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, ...;
type
TForm2 = class(TForm)
...
procedure PopupMenu1Popup(Sender: TObject);
private
function Hook(code: Integer; w: WPARAM; p: LPARAM): Lresult stdcall;
end;
var
Form2: TForm2;
HookID: hhook;
implementation
{$R *.dfm}
function TForm2.Hook(code: Integer; w: WPARAM; p: LPARAM): Lresult stdcall;
begin
if code < 0 then
begin
Result := CallNextHookEx(0, code, w, p);
Exit;
end;
Result := CallNextHookEx(0, code, w, p);
end;
procedure TForm2.PopupMenu1Popup(Sender: TObject);
begin
HookID := SetWindowsHookEx(WH_KEYBOARD, @TForm2.Hook, 0,
GetWindowThreadProcessId(PopupList.Window, nil));
end;
end.
Альтернативный ответ и комментарии
Использование SetWindowsHookEx для перехвата сообщений клавиатуры, отправляемых в всплывающее меню, не является лучшим решением. Рекомендуется использовать подкласс окна, используя функцию SetWindowSubclass или SetWindowLongPtr. Более предпочтительный вариант - заменить объект PopupList на кастомный объект, который переопределяет метод WndProc.
Подтвержденный ответ и исправление
Проблема заключается в том, что функция Hook является методом класса TForm2 и, следовательно, имеет скрытый параметр self. Функцию следует разместить вне класса TForm2, или сделать её статической функцией класса.
function Hook(code: Integer; w: WPARAM; p: LPARAM): Lresult stdcall; static;
Также в коде присутствуют избыточные строки, которые можно убрать, вызывая CallNextHookEx() всегда.
Пример исправленного кода
unit Unit2;
interface
uses
Winapi.Windows, Winapi.Messages, ...;
type
TForm2 = class(TForm)
...
procedure PopupMenu1Popup(Sender: TObject);
end;
var
Form2: TForm2;
HookID: hhook;
function Hook(code: Integer; w: WPARAM; p: LPARAM): Lresult stdcall; static;
implementation
{$R *.dfm}
function Hook(code: Integer; w: WPARAM; p: LPARAM): Lresult stdcall; static;
begin
Result := CallNextHookEx(0, code, w, p);
end;
procedure TForm2.PopupMenu1Popup(Sender: TObject);
begin
// Логика подключения хука может быть здесь
end;
end.
Заключение
При работе с хуками в Delphi важно правильно обращаться с параметрами и контекстом вызова функций. В данном случае, перемещение функции Hook за пределы класса TForm2 и устранение ненужных условий в её теле позволит избежать ошибки доступа.
Пользователь столкнулся с проблемой при попытке отслеживания нажатия клавиши F11 в всплывающем меню компонента TrayIcon в Delphi, используя функцию `SetWindowsHookEx`, и получил ошибку доступа при записи в определенный адрес памяти.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.