Я пытаюсь использовать SetWindowsHookEx
для настройки перехвата WH_SHELL
, чтобы получать уведомления о системных событиях HSHELL_WINDOWCREATED
и HSHELL_WINDOWDESTROYED
. Я передаю 0 для последнего аргумента dwThreadId
, который, согласно документам , должен "связать процедуру подключения со всеми существующими потоками, работающими на том же рабочем столе, что и вызывающий поток". Я также передаю дескриптор моей DLL (HInstance
в Delphi) для параметра hMod
, как и все примеры, на которые я смотрел.
Тем не менее, я только когда-либо получаю уведомления об окнах, созданных моим собственным приложением, и - чаще всего - мои тесты приводят к тому, что процесс на рабочем столе падает, когда я закрываю свое приложение. Прежде чем вы спросите, я звоню UnhookWindowsHookEx
. Я также всегда вызываю CallNextHookEx
из моего обработчика.
Я запускаю тестовое приложение с ограниченной учетной записью пользователя, но пока не нашел никаких подсказок, указывающих на то, что это сыграет свою роль ... (хотя это на самом деле меня удивляет)
AFAICT, я сделал все по книге (очевидно, я не сделал, но до сих пор не вижу, где).
Я использую Delphi (2007), но это не должно иметь большого значения, я думаю.
РЕДАКТИРОВАТЬ: Может быть, я должен был упомянуть об этом раньше: я загрузил и попробовал несколько примеров (хотя, к сожалению, не так много доступных для Delphi - особенно ни один для WH_SHELL
или WH_CBT
). Хотя они и не рушат систему, как это делает мое тестовое приложение, они по-прежнему не захватывают события из других процессов (хотя я могу проверить с помощью ProcessExplorer, что они загружены в них в порядке). Таким образом, кажется, что либо с моей конфигурацией системы что-то не так, либо примеры неверны, либо просто невозможно захватить события из других процессов. Может ли кто-нибудь просветить меня?
EDIT2: Хорошо, вот источник моего тестового проекта.
DLL, содержащая процедуру подключения:
library HookHelper;
uses
Windows;
{$R *.res}
type
THookCallback = procedure(ACode, AWParam, ALParam: Integer); stdcall;
var
WndHookCallback: THookCallback;
Hook: HHook;
function HookProc(ACode, AWParam, ALParam: Integer): Integer; stdcall;
begin
Result := CallNextHookEx(Hook, ACode, AWParam, ALParam);
if ACode < 0 then Exit;
try
if Assigned(WndHookCallback)
// and (ACode in [HSHELL_WINDOWCREATED, HSHELL_WINDOWDESTROYED]) then
and (ACode in [HCBT_CREATEWND, HCBT_DESTROYWND]) then
WndHookCallback(ACode, AWParam, ALParam);
except
// plop!
end;
end;
procedure InitHook(ACallback: THookCallback); register;
begin
// Hook := SetWindowsHookEx(WH_SHELL, @HookProc, HInstance, 0);
Hook := SetWindowsHookEx(WH_CBT, @HookProc, HInstance, 0);
if Hook = 0 then
begin
// ShowMessage(SysErrorMessage(GetLastError));
end
else
begin
WndHookCallback := ACallback;
end;
end;
procedure UninitHook; register;
begin
if Hook <> 0 then
UnhookWindowsHookEx(Hook);
WndHookCallback := nil;
end;
exports
InitHook,
UninitHook;
begin
end.
И основная форма приложения с помощью крючка:
unit MainFo;
interface
uses
Windows, SysUtils, Forms, Dialogs, Classes, Controls, Buttons, StdCtrls;
type
THookTest_Fo = class(TForm)
Hook_Btn: TSpeedButton;
Output_Lbx: TListBox;
Test_Btn: TButton;
procedure Hook_BtnClick(Sender: TObject);
procedure Test_BtnClick(Sender: TObject);
public
destructor Destroy; override;
end;
var
HookTest_Fo: THookTest_Fo;
implementation
{$R *.dfm}
type
THookCallback = procedure(ACode, AWParam, ALParam: Integer); stdcall;
procedure InitHook(const ACallback: THookCallback); register; external 'HookHelper.dll';
procedure UninitHook; register; external 'HookHelper.dll';
procedure HookCallback(ACode, AWParam, ALParam: Integer); stdcall;
begin
if Assigned(HookTest_Fo) then
case ACode of
// HSHELL_WINDOWCREATED:
HCBT_CREATEWND:
HookTest_Fo.Output_Lbx.Items.Add('created handle #' + IntToStr(AWParam));
// HSHELL_WINDOWDESTROYED:
HCBT_DESTROYWND:
HookTest_Fo.Output_Lbx.Items.Add('destroyed handle #' + IntToStr(AWParam));
else
HookTest_Fo.Output_Lbx.Items.Add(Format('code: %d, WParam: $%x, LParam: $%x', [ACode, AWParam, ALParam]));
end;
end;
procedure THookTest_Fo.Test_BtnClick(Sender: TObject);
begin
ShowMessage('Boo!');
end;
destructor THookTest_Fo.Destroy;
begin
UninitHook; // just to make sure
inherited;
end;
procedure THookTest_Fo.Hook_BtnClick(Sender: TObject);
begin
if Hook_Btn.Down then
InitHook(HookCallback)
else
UninitHook;
end;
end.