Привет всем
Надо поставить хук на создание окон. Нашел (и слегка модифицировал) такой код:
var HookHandle: hHook;
function CBTProc(Code: integer; WParam: word; LParam: Integer): Longint; stdcall;
type PCBT_CREATEWND=^CBT_CREATEWND;
var s: pchar;
z: PCBT_CREATEWND;
ok: Integer;
s2:string;
i:byte;
begin
if Code<0 then begin
Result := CallNextHookEx(HookHandle, code, WParam, LParam);
exit;
end;
case Code of
HCBT_CREATEWND: begin
z:=PCBT_CREATEWND(LParam);
s:=z.lpcs.lpszName;
if s<>nil then begin
s2:='';
for i:=1 to length(s) do begin
s2:=s2+s^;
inc(s);
end;
if pos('Блокнот',s2)>0 then begin
Result:=1;
exit;
end;
end;
end;
end;
Result := 0;
end;
begin
HookHandle := SetWindowsHookEx(WH_CBT, @CBTProc, HInstance, 0);
end;
Хм. Интересно, куда подевались все вопросительные знаки? Ты хоть один видишь? Я почему-то нет. Вопрос-то в чем?
В том, что код не работает как надо ) (по идее, при открытии блокнота блокнот не должен открыться).
Нуу.. я изменил code на WH_Shell, этого достаточно? Пока всё равно не ловит.
Дык это только в примере блокнот, может я ещё чего-нибудь запретить захочу, универсальность нужна..
Пошукал в msdn, кажется надо так примерно:
function CBTProc(Code: integer; WParam: word; LParam: Integer): Longint; stdcall;
type PCBT_CREATEWND=^CBT_CREATEWND;
var s: pchar;
s2:string;
i:byte;
z: PCBT_CREATEWND;
begin
if Code<0 then begin
Result := CallNextHookEx(HookHandle, code, WParam, LParam);
exit;
end;
case code of
HSHELL_WINDOWCREATED: begin
z:=PCBT_CREATEWND(HSHELL_LANGUAGE); //hshell_language по мсдн должен возвращать хэндл окна
s:=z.lpcs.lpszName;
if s<>nil then begin
s2:='';
for i:=1 to length(s) do begin
s2:=s2+s^;
inc(s);
end;
if pos('Блокнот',s2)>0 then begin
Result:=1;
exit;
end;
end;
end;
end;
Result := 0;
end;
begin
HookHandle := SetWindowsHookEx(WH_SHELL, @CBTProc, HInstance, 0);
end.
Что значит, нет Хэндла? Я где-то тебе сказал про то ,что нет хэндла? Я сказал, что на момент перехвата HCBT_CREATEWND тебе не гарантируется наличие заголовка окна. Хэндл есть всегда, он содержится в wParam:
А у меня есть хитроумная функция, которая выдаст путь к файлу по хэндлу
uses tlhelp32;
Function HandtoPath(H: HWND): String;
Var
Pid:Cardinal;
M: TModuleEntry32;
HSnapshot: THandle;
Begin
Result:='';
GetWindowThreadProcessId(H,@Pid);
HSnapshot:=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,Pid);
If HSnapshot=-1 Then Exit;
M.DwSize:=SizeOf(TModuleEntry32);
If Module32First(HSnapshot,M) Then Result:=M.SzExePath;
CloseHandle(HSnapshot);
End;
И правда, по классу лучше определять.. что-то я не подумал) Тогда так:
function getclname(wnd:hwnd):string;
var
Cl:array[0..pred(MAX_PATH)] of char;
begin
GetClassName(wnd,cl,MAX_PATH);
result:=cl;
end;
function CBTProc(Code: integer; WParam: word; LParam: Integer): Longint; stdcall;
begin
if Code<0 then begin
Result := CallNextHookEx(HookHandle, code, WParam, LParam);
exit;
end;
case Code of
HCBT_CREATEWND: begin
showmessage(getclname(wparam));
end;
end;
Result := 0;
end;
begin
HookHandle := SetWindowsHookEx(WH_CBT, @CBTProc, HInstance, 0);
end;
Можешь прикрепить весь проект?
Он на "любимом" KOL... Надо? Но ловушка точно ставится, я проверял, проходит куча сообщений через неё.
И да, зеркальные классы не виноваты, я в них и с сообщениями работал, и с теми же ловушками когда-то..
added: сейчас воткнул функцию в обычный проект D2007 - то же самое.
Хм... Надо было все-таки исходники попросить у тебя...
Ту би контин, блин.. Короче, такая dll:
library project1;
uses
sysutils,windows;
{$R *.res}
var hookhandle:HHook;
function getclname(wnd:hwnd):string;
var
Cl:array[0..pred(MAX_PATH)] of char;
begin
GetClassName(wnd,cl,MAX_PATH);
result:=cl;
end;
function CBTProc(Code: integer; WParam: word; LParam: Integer): Longint; stdcall;
begin
if Code<0 then begin
Result := CallNextHookEx(HookHandle, code, WParam, LParam);
exit;
end;
case Code of
HCBT_CREATEWND:begin
if pos('NOTEPAD',uppercase(getclname(wparam)))>0 then begin
result:=1;
exit;
end;
end;
end;
Result := 0;
end;
procedure LocalExitProc; far;
begin
if HookHandle<>0 then begin
UnhookWindowsHookEx(HookHandle);
end;
end;
exports CBTProc;
begin
end.
function hoks(Code: integer; WParam: word; LParam: Integer): Longint; stdcall; external 'project1.dll' name 'CBTProc';
// также пробовал с таким прототипом: procedure hoks()
...
Procedure sethook;
begin
HookHandle := SetWindowsHookEx(WH_CBT, @hoks, HInstance, 0);
end;
Заставил ты меня все-таки написать работающий хук на FPC ...
Вот DLL-ка:
{$mode delphi}
Library Proj;
uses
SysUtils, Windows;
var
HookHandle: HHook;
function GetClName(myWnd: HWND): String;
var
Cl: array[0 .. pred(MAX_PATH)] of char;
begin
GetClassName(myWnd, Cl, MAX_PATH);
result := cl;
end;
function CBTProc(Code: integer; myWParam: WPARAM;
myLParam: LPARAM): LRESULT; stdcall;
begin
if Code < 0 then
begin
Result := CallNextHookEx(HookHandle, code, myWParam, myLParam);
exit;
end;
case Code of
HCBT_CREATEWND:
begin
if pos('NOTEPAD', UpperCase(GetClName(myWParam))) > 0 then
begin
result := 1; exit;
end;
end;
end;
result := 0;
end;
procedure SetTheHook; stdcall;
begin
if HookHandle = 0 then
begin
HookHandle := SetWindowsHookEx(WH_CBT, @CBTProc, HInstance, 0);
end;
end;
procedure DelTheHook; stdcall;
begin
if HookHandle<>0 then
begin
UnhookWindowsHookEx(HookHandle);
end;
end;
exports
SetTheHook, DelTheHook;
end.
const
DLLName = 'proj.dll';
procedure SetTheHook; stdcall; external DLLname;
procedure DelTheHook; stdcall; external DLLname;
// ...
begin // Основной блок программы
if not WinRegister then begin
MessageBox(0, 'Register failed', nil, mb_Ok);
Exit;
end;
hWindow := WinCreate;
if longint(hWindow) = 0 then begin
MessageBox(0, 'WinCreate failed', nil, mb_Ok);
Exit;
end;
SetTheHook;
while GetMessage(@AMessage, 0, 0, 0) do begin
TranslateMessage(AMessage);
DispatchMessage(AMessage);
end;
DelTheHook;
Halt(AMessage.wParam);
end.
У меня почти то же самое было, только это работает)) Я понял, надо было в основной программе сразу писать процедуру из exports, без всяких name 'CBTProc'.. Большое спасибо Всё чаще, когда у меня какой-то затык, я лезу смотреть созданные мною темы..))
Кстати, чтобы не таскать с собой отдельно dll - только извлекать из ресурсов в нужный момент? Прилинковать как-нибудь нельзя?
Простенький ксор перед добавлением в ресурсы, и после извлечения обратная операция - помогают И потом такая тишина..)
Ещё вопрос, относительно "обратной связи". Мне в основной программе нужно узнавать, как там дела у ловушки, что она поймала и т.п. Но т.к. я не нашёл приём экспортирования переменных (наверное, этого вообще нельзя делать), то написал функцию, которая возвращает значение переменной. Так:
Function retb:integer;stdcall
begin
result:=b;
end;
function retb:byte; stdcall; external mydlname;
офигеть, из-за одной переменной столько кода непонятного! Неужели не предусмотрено что-то попроще? (хотя, гугл намекает, что нет). Может, можно описать во входных параметрах SetHook(var b:integer), а потом, в процедуре, присваивать? (да, так не нужно писать программы))
Вообще, самый элегантный метод - использовать txt в качестве посредника) Сработало - создал, принял - удалил..
Если нужно просто возвращать индекс заблокированного окна, вот тебе еще один способ, навскидку (не тестировал, но явных причин не работать - не вижу, да и получше будет, чем с txt-файлами). У тебя ж хендл твоего окна не меняется после того, как ты хук установил? Вот в SetHook передавай хендл своего окна (главного окна приложения, либо какой-то формы, я не знаю, что там у тебя), а при блокировке приложения ловушкой - PostMessage этому окну, "так мол и так - заблокировано приложение <и информация о нем>", а в оконной функции своего приложения лови эти сообщения и обрабатывай как нужно. Лучше будет, чем с текстовыми файлами, да еще по таймеру заморачиваться. К таймеру бы вообще привязываться не надо.
Вот так шлю:
const wm_user=$0400; //в библиотеке эта константа почему-то была undeclared, юнит windows есть
begin
postmessage(whan,$0400+50000,i,0);//+50000 взял, чтоб наверняка попасть в [0xC000 ; 0xFFFF].
end;
function TForm1.KOLFormMessage(var Msg: tagMSG;
var Rslt: Integer): Boolean;
begin
if msg.message=WM_USER+50000 then begin
//...используем wparam
result:=false; //дальше сообщению хода нет
end;
end;
postmessage(whan, WM_USER + 1, i, 0);
Очевидное-невероятное, блин! Передаю в sethook хэндл:
procedure SetTheHook(h:hwnd); stdcall;
begin
whan:=h;
if HookHandle = 0 then
begin
HookHandle := SetWindowsHookEx(WH_CBT, @CBTProc, HInstance, 0);
end;
end;
В общем, если не хочешь потерять еще три дня, и потом все-таки вернуться к тому, что я тебе написал в сообщении №25 - вернись сейчас. ТОЛЬКО MMF гарантируют тебе работу. Память должна быть общей (shared memory). Вот тебе еще один пример: http://webcache.googleusercontent.com/search?q=cache:cMFnZHZKRjAJ:www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_22867686.html+%2BHCBT_CREATEWND+%2Bpostmessage&cd=5&hl=ru&ct=clnk (начиная со слов "this is real working example". Проверено, действительно работает). Больше прописные истины повторять не буду. Хочешь экспериментировать - экспериментируй. Как надоест - скажешь.
Короче на данный момент я остановился на текстовике. Логика программы предполагает, что если она вообще работает, то текстовик хватит прав создать... Просто из-за одной переменной добавлять (и разбирать) кода столько, сколько наверное во всей программе нет - нерационально как-то, что ли.. Наверное, я ещё вернусь к этой теме, когда будет "рациональней"..
volvo, ещё раз спасибо за советы, без них я бы тут далеко не уехал
И всё же как-то странно, зачем нужна возможность создания глобальных переменных в dll, если такие дела с ними..
library myhook;Сколько кода добавлено? 10 строк? Если что - вот VCL-ный проект, который ставит хук и получает сообщение о том, что окно было заблокировано (проект с DLL-кой тоже вложен):
{ Тут было длинное предупреждение... }
uses
SysUtils, Windows, Messages;
{$R *.res}
type
PHookRec = ^THookRec;
THookRec =
record
AppWindow: HWND;
HookID: HHOOK;
end;
const
MY_MESSAGE = WM_USER + 1;
var
RHookRec: PHookRec = nil;
function GetClName(myWnd: HWND): String;
var
Cl: array[0 .. pred(MAX_PATH)] of char;
begin
GetClassName(myWnd, Cl, MAX_PATH);
result := cl;
end;
function CBTProc(Code: integer; myWParam: WPARAM;
myLParam: LPARAM): LRESULT; stdcall;
begin
if Code < 0 then
begin
Result := CallNextHookEx(RHookRec^.HookID, code, myWParam, myLParam);
exit;
end;
case Code of
HCBT_CREATEWND:
begin
if pos('NOTEPAD', UpperCase(GetClName(myWParam))) > 0 then
begin
PostMessage(RHookRec^.AppWindow, MY_MESSAGE, myWParam, 0);
result := 1; Exit;
end;
end;
end;
result := 0;
end;
procedure SetTheHook(Handle: HWND); stdcall;
begin
RHookRec^.AppWindow := Handle;
RHookRec^.HookID := SetWindowsHookEx(WH_CBT, @CBTProc, HInstance, 0);
end;
procedure DelTheHook; stdcall;
begin
UnhookWindowsHookEx(rHookRec^.HookID);
end;
{$J+}
procedure EntryPointProc(Reason: Integer);
const
hMapObject: THandle = 0;
begin
case reason of
DLL_PROCESS_ATTACH:
begin
hMapObject :=
CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(THookRec), 'volvo_CBT');
rHookRec := MapViewOfFile(hMapObject, FILE_MAP_ALL_ACCESS, 0, 0, SizeOf(THookRec));
end;
DLL_PROCESS_DETACH:
begin
UnMapViewOfFile(rHookRec);
CloseHandle(hMapObject);
end;
DLL_THREAD_ATTACH,
DLL_THREAD_DETACH: ;
end;
end;
exports
SetTheHook, DelTheHook;
begin
DllProc := @EntryPointProc;
EntryPointProc(DLL_PROCESS_ATTACH);
end.
Кажется понял, в библиотеке просто делается указатель на одну общую структуру, и какие-то пассы при инициализации)
Спасибо, сделал по-человечески) Только такой способ кажется не подходит для динамической подгрузки DLL? Делаю так:
type
Tsetthehook = procedure(Handle: HWND);stdcall;
Tdelthehook = procedure;stdcall;
var setthehook:TSetthehook;
delthehook:TDelthehook;
DLLInstance : THandle;
Procedure loadlib;
begin
try
DLLInstance := LoadLibrary(pchar(writepath+mydlname));
if DLLInstance = 0 then killmeplz;
@setthehook := GetProcAddress(DLLInstance, 'SetTheHook');
if @setthehook <> nil then setthehook(form1.form.handle) else killmeplz;
@delthehook := GetProcAddress(DLLInstance, 'DelTheHook');
finally
FreeLibrary(DLLInstance);
end;
end;
Procedure loadlib;Работает, зараза
begin
DLLInstance := LoadLibrary(DLLname);
if DLLInstance = 0 then ShowMessage('Cannot Load DLL')
else
begin
@setthehook := GetProcAddress(DLLInstance, 'SetTheHook');
@delthehook := GetProcAddress(DLLInstance, 'DelTheHook');
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
loadlib;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeLibrary(DLLInstance);
end;
Все-таки решил добавить. Интересная и познавательная информация о написании своих DLL:
http://www.transl-gunsmoker.ru/2009/01/dllmain_05.html
, и по ссылкам оттуда:
http://www.transl-gunsmoker.ru/2009/01/dllmain.html
http://www.transl-gunsmoker.ru/2009/01/dllmain_04.html
http://www.transl-gunsmoker.ru/2009/01/dllmain_7983.html
И ещё незадача.. я тут пробовал на разные приложения хук нацеливать, и заметил, что некоторые (это не антивирусы, на них не пробовал ), когда запрещаешь их создание в хуке (result:=1), выдают звук ошибки, и вдобавок вылетает ошибка windows (отправить отчёт\не отправлять). С этим можно что-нибудь сделать?
"Некоторые" - это какие, например? (желательно - из самых известных, чтоб можно было проверить) Что сообщает Системный Журнал в таком случае, из-за чего ошибка произошла?
Например мессенджер QIP, в журнале такое: Ошибка приложения qip.exe, версия 8.0.8.1, модуль kernel32.dll, версия 5.1.2600.2180, адрес 0x0001eb33.
И какой класс окна ты запрещаешь, чтоб "погасить" QIP?
Добавлено через 4 мин.
Кстати, вот тут: http://www.ms-news.net/f3289/cbtproc-hcbt_createwnd-2737174.html говорится, что подобное поведение может быть признаком не совсем правильно написанной программы:
Ну для инфиума, например - TFRMMAN (в полном названии ещё был суффикс .unicode, ну там всё равно pos используется).
madToolsMsgHandlerWindow$a28$40cf4c
madToolsMsgHandlerWindow$a28$40cf4c
TApplication
IME
TPUtilWindow
TPUtilWindow
TPUtilWindow
GDI+ Hook Window Class
IME
OleMainThreadWndClass
TPUtilWindow
TPUtilWindow
TPUtilWindow
TfrmCore.UnicodeClass
TPUtilWindow
TPUtilWindow
TPUtilWindow
TPUtilWindow
TPUtilWindow
TPUtilWindow
TPUtilWindow
TPUtilWindow
TPUtilWindow
TPUtilWindow
TPUtilWindow
TPUtilWindow
TPUtilWindow
xxxxWorker
TPUtilWindow
TPUtilWindow
TPUtilWindow
TPUtilWindow
TPUtilWindow
TfrmMan.UnicodeClass
TInfuButton.UnicodeClass
TInfuComboBox.UnicodeClass
ComboLBox
Edit
CicMarshalWndClass
MSCTFIME UI
TInfuBitBtn.UnicodeClass
TInfuEdit.UnicodeClass
TInfuCheckBox.UnicodeClass
TPUtilWindow
TPUtilWindow
TfrmNewRegWiz.UnicodeClass
TPUtilWindow
IcsWndControlWindowClass
IcsWndControlWindowClass
IME
TfrmNewRegWiz.UnicodeClass
TInfuPanel.UnicodeClass
TInfuPanel.UnicodeClass
TInfuBitBtn.UnicodeClass
TInfuBitBtn.UnicodeClass
TInfuBitBtn.UnicodeClass
TInfuBitBtn.UnicodeClass
TInfuBitBtn.UnicodeClass
TInfuBitBtn.UnicodeClass
TInfuBitBtn.UnicodeClass
TInfuBitBtn.UnicodeClass
Фигассе я наивный... А можно название программки, которая этот лог сделала? Или можно воспользоваться этим же хуком, фильтруя ClassName'ы квипа?
И чего теперь, мне атомную бомбу на всю эту толпу кидать?))
function CBTProc(Code: integer; myWParam: WPARAM;
myLParam: LPARAM): LRESULT; stdcall;
var
className: string;
ACopyData: TCopyDataStruct;
begin
if Code < 0 then
begin
Result := CallNextHookEx(rHookRec^.HookID, code, myWParam, myLParam);
exit;
end;
case Code of
HCBT_CREATEWND:
begin
className := GetClName(myWParam);
with aCopyData do
begin
dwData := 0;
cbData := Length(className) * SizeOf(Char);
lpData := PChar(className);
end;
SendMessage(RHookRec^.AppWindow, WM_COPYDATA, 0, LPARAM(@ACopyData));
if pos(UpperCase('notepad'), UpperCase(GetClName(myWParam))) > 0 then // или className
begin
PostMessage(RHookRec^.AppWindow, MY_MESSAGE, myWParam, 0);
result := 1; Exit;
end;
end;
end;
result := 0;
end;
// в классе формы - заголовокустанавливаешь хук и запускаешь любое приложение значком быстрого вызова, который лежит на десктопе, чтоб лишние окна не создавались - получаешь список всех классов, созданных при запуске приложения.
procedure WMCopyData(var Msg: TWMCopyData); message WM_COPYDATA;
// и реализация
procedure TForm1.WMCopyData(var Msg: TWMCopyData);
var
Info: string;
Len: Integer;
begin
Len := Msg.CopyDataStruct.cbData div sizeof(Char);
SetLength(Info, Len);
Move(Msg.CopyDataStruct.lpData^, PChar(Info)^, Len * sizeof(Char));
Memo1.Lines.Add(Info);
end;
Хм.. попробовал тормозить все классы, стоящие до TfrmMan (и вместе, и по отдельности - они там повторяются, кстати) - окошко квипа так же появлялось, а если тормозить и TfrmMan - то все, багрепорт) Я вот чего думаю, поставил я хук, ведь пока сообщение не пройдет все ловушки, к квипу не придёт? Можно ведь "приостанавливать" процесс запуска, а в это время убивать процесс, хэндл-то есть... и хитроумная процедура тоже)
Добавлено через 15 мин.
Да, так получается, в принципе.. Ловлю самый первый класс и завершаю процесс. Правда, тут применяю и первую "хитроумную" процедуру, для получения пути к файлу, т.к. для завершения процесса второй хитроумной нужно знать имя exe. У любителей superqip.exe не сработает, правда..
if (pos(UpperCase('tapplication'), UpperCase(GetClName(myWParam))) > 0) or
(pos(UpperCase('madToolsMsgHandler'), UpperCase(GetClName(myWParam))) > 0) or
(pos(UpperCase('madExceptWndClass'), UpperCase(GetClName(myWParam))) > 0) or
(pos(UpperCase('tfrmcore'), UpperCase(GetClName(myWParam))) > 0) then
begin
PostMessage(RHookRec^.AppWindow, MY_MESSAGE, myWParam, 0);
result := 1; Exit;
end;
Кажется, поправку на ветер забыл У меня багрепорта теперь нет, но звук ошибки всё равно булькает.) Тоже по умолчанию выпрыгивает окно выбора аккаунта. Кстати, на единичном уничтожении tfrmcore у меня уже всё крошилось) А чем с теоретической точки зрения плох мой вариант, если не считать перенесение exe в произвольную папку?
В предыдущем посте три смайла образуют равнобедренный тупоугольный треугольник, тонко
//uses tlhelp32;
function KillTask(ExeFileName: string): integer;stdcall; //вроде как это из тех 10% кода из сети, который не..
const
PROCESS_TERMINATE=$0001;
var
ContinueLoop: BOOL;
FSnapshotHandle: THandle;
FProcessEntry32: TProcessEntry32;
begin
result := 0;
FSnapshotHandle := CreateToolhelp32Snapshot
(TH32CS_SNAPPROCESS, 0);
FProcessEntry32.dwSize := Sizeof(FProcessEntry32);
ContinueLoop := Process32First(FSnapshotHandle,
FProcessEntry32);
while integer(ContinueLoop) <> 0 do
begin
if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
UpperCase(ExeFileName))
or (UpperCase(FProcessEntry32.szExeFile) =
UpperCase(ExeFileName))) then
Result := Integer(TerminateProcess(OpenProcess(
PROCESS_TERMINATE, BOOL(0),
FProcessEntry32.th32ProcessID), 0));
ContinueLoop := Process32Next(FSnapshotHandle,
FProcessEntry32);
end;
CloseHandle(FSnapshotHandle);
end;
function CBTProc(Code: integer; myWParam: WPARAM;
myLParam: LPARAM): LRESULT; stdcall;
var i:integer;
s:string;
begin
if Code < 0 then
begin
Result := CallNextHookEx(RHookRec^.HookID, code, myWParam, myLParam);
exit;
end;
case Code of
HCBT_CREATEWND:
begin
//s:=getclname(mywparam);
sn:=uppercase(handtopath(mywparam));
for i:=1 to fc do begin
if pos(wins[i],sn)>0 then begin
PostMessage(RHookRec^.AppWindow, MY_MESSAGE,wparam(i), 0);
killtask(extractfilename(sn));
break;
end;
end;
end;
end;
result := 0;
end;
Ну не знаю... Ловить HCBT_CREATEWND, и вместо того, чтобы просто вернуть 1 - вызывать стороннюю процедуру, которая будет уничтожать процессы через TerminateProcess (который был, кстати, создан совсем для других целей) - это как-то... Входит как раз в 90% Да, а что насчет работы под Win64? Уверен?
Кстати, если уж TerminateProcess - то перебор всех процессов совсем не обязательно делать, достаточно:
case Code of, это точно так же снесет QIP... Останется только провести мышой над треем (или каким-то образом обновить трей, чтоб иконка убитого приложения ушла оттуда).
HCBT_CREATEWND:
begin
if (pos(UpperCase('tfrmman'), UpperCase(GetClName(myWParam))) > 0) then
begin
PostMessage(RHookRec^.AppWindow, MY_MESSAGE, myWParam, 0);
GetWindowThreadProcessId(myWParam, PID);
TerminateProcess(OpenProcess(PROCESS_TERMINATE, BOOL(0), PID), 0);
result := 1; Exit;
end;
end;
Ловить HCBT_CREATEWND, и вместо того, чтобы просто вернуть 1
Ну да, трею, разумеется...