![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
WH_MOUSE - troubleshooting + BONUS (Вопрос о приведении типов). [D6, Win95/98, Win2k]
Fellomena © (2002-05-28 11:16:00)Всем привет! Раньше, когда надо было отлавливать события мыши я не
мучаясь ставила WH_GETMESSAGE и всё было Ok:
-------------------------------------------
library MHook;
uses
Windows, Messages, SysUtils;
var
hHook : integer = 0;
function MouseProc(code : integer; wParam : word; lParam : longint) : longint; stdcall;
var
x, y: integer;
s: string;
begin
CallNextHookEx(0, code, wParam, lParam);
Result:= 0;
if code= HC_ACTION then
begin
if TMsg(Pointer(lParam)^).message = WM_RBUTTONDOWN then
begin
x:= TMsg(Pointer(lParam)^).pt.X;
y:= TMsg(Pointer(lParam)^).pt.Y;
s:= "HOOK is working ! X= " + IntToStr(x) + ", Y= " + IntToStr(y);
MessageBox(0, PChar(s), PChar("Message"), 0); {в реале отсылка WM_USER HOST приложению со значениями координат}
end;
end;
end;
Procedure SetHook(flag1: boolean) export; stdcall;
begin
if flag1 then
hHook:= SetWindowsHookEx(WH_GETMESSAGE, @MouseProc, hinstance, 0)
else
begin
UnHookWindowsHookEx(hHook);
MessageBox(0, PChar("Hook disabled"), PChar("Message"), 0);
hHook:= 0;
end;
end;
exports
SetHook;
begin
end.
-------------------------------------------
На CallNextHookEx не ругайтесь - под Win2000 работает всё отлично - МелкоМягкие переделали таки реализацию.
Под Win9x использую MMF - тут всё Ok.
(может где в коде лаги незначительные - sorry, пишу по памяти, но общая картина такая).
Недавнорешила попробовать использовать WH_MOUSE:
-------------------------------------------
library MHook;
uses
Windows, Messages, SysUtils;
var
hHook : integer = 0;
function MouseProc(code : integer; wParam : word; lParam : longint) : longint; stdcall;
var
x, y: integer;
s: string;
begin
CallNextHookEx(0, code, wParam, lParam);
Result:= 0; // сообщение дальше пошло
if code= HC_ACTION then
begin
if wParam= WM_RBUTTONDOWN then
begin
x:= //получение значений координат;
y:= //получение значений координат;
s:= "HOOK is working ! X= " + IntToStr(x) + ", Y= " + IntToStr(y);
MessageBox(0, PChar(s), PChar("Message"), 0);
end;
end;
end;
Procedure SetHook(flag1: boolean) export; stdcall;
begin
if flag1 then
hHook:= SetWindowsHookEx(WH_MOUSE, @MouseProc, hinstance, 0)
else
begin
UnHookWindowsHookEx(hHook);
MessageBox(0, PChar("Hook disabled"), PChar("Message"), 0);
hHook:= 0;
end;
end;
exports
SetHook;
begin
end.
-------------------------------------------
Так вот, всё компилится нормально, но не работает, сдаётся мне, что if wParam= WM_RBUTTONDOWN then...- тут что-то не так.
Подскажите почему это не правильно, в help-е написанно, что в wParam у WH_MOUSE лежит именно Mouse event.
Или надо явно привести wParam к типу event? У меня ничего не вышло 8(
BONUS:
Как достоверно узнать - в каких случаях явное приведение типов необходимо, а в каких optional?
Пример:LoadLibrary("MyDll.dll");иLoadLibrary(PChar("MyDll.dll"));И то и то будет работать, но я всегда использую второй вариант - так надёжнее.
Или ещё:MessageBox(0, PChar("Hi!"), PChar(Form1.Caption), 0);тоже можно явно не приводить, работать будет.
А вот если не сделатьx:= TMsg(Pointer(lParam)^).pt.X;а сделатьx:= lParam^.pt.X;то работать не будет.
Как определить как в данном случае правильно?
Fellomena (2002-05-31 15:56:00) [1]2 Raptor:
>> "Если не нравится можно не отсылать сообщения, а обрабатывать все прямо в хуке, это не принципиально."
Хех 8))) В том-то и дело - не получается прямо в хуке и всё тут 8)))
А против MMF я ничего против не имею и тоже его использую как в Win9x так и в Win2k.
Вот кончатся экзамены - разберусь я с этим WH_MOUSE как пить дать.
Raptor (2002-05-30 17:31:00) [2]2 Fellomena (29.05.02 12:09)
> 2 Raptor © (29.05.02 00:03):
> sorry, но 99% приведённого тобой кода - вода, связанная
> с MMF и механизмом межпроцессорной связи, которую я намеренно
> опустила. А вот это достаточно грубо, imho:
>
> При каждом "мышином" событии отсылать WM_USER - ресурсов не
> жалко?
Возможно ты и права. Но у меня эта "вода" работает как на моей Win2k так и на рядом стоящей Win98. А у тебя, судя по всему, нет. ;-))
Посему советую не полагаться на MS и на ее исправления, а воспользоваться старым и проверенным способом, тоесть использовать MMF.
А насчет грубости, так это же только демонстрация, которая наглядно показывает, что все работает нормально. Если не нравится можно не отсылать сообщения, а обрабатывать все прямо в хуке, это не принципиально.
Digitman (2002-05-29 13:14:00) [3]>>"..но не работает"
Это можно трактовать как угодно, в.т. и невыполнение тела callback вообще.
Разумеется, ты получишь кучу MessageBox() - ты же никак не анализируешь источник события нажатия клавиши мыши (в дан.случае - клик в зоне активного окна IE), а просто фиксируешь сам факт его возникновения (вернее - его постановку в очереди сообщений окон каждого из активных процессов) выбросом MessageBox()
Проецирование чего-то там на поток - это нонсенс. Проецирование кодового сегмента DLL на вирт.АП процесса - это другое дело. В твоем случае будет создана проекция образа DLL на АП хост-процесса, коим будет являться процесс, выполняющий целевую загрузку этой DLL (т.е. процесс, который планирует вызов ф-ции SetHook). Весь кодовый сегмент DLL, в т.ч. и фрагмент с телом callback-ф-ции, будет спроецирован на АП этого процесса. В АП прочих активных процессов в системе никаких преобразований не происходит : диспетчеризацией и вызовами callback-ф-ции будет управлять сама система - только она знает, в АП какого конкретно процесса расположена точка входа в ф-цию
Fellomena (2002-05-29 12:09:00) [4]2 Digitman © (28.05.02 18:25):
Да я и не говорила, что тело callback ф-ии не выполняется 8)
MessageBox как раз таки работает, но как-то странно: при нажатии
на окне Explorer-а по идее должно выводится одно сообщение, а их выводится столько, сколько процессов спроецировали на своё АП мою DLL, т.е. все загруженные.
После этого комп виснет намертво (Win9x).
На рядомстоящем Win2000 таже картина, но без подвисания.
Попутный вопрос: DLL с callback func проецируется в первичный поток процесса или на все? (по идее на все, конечно - это же глобальный хук).
А если так, то срабатывание callback ф-ии в родительском потоке не приводит к её срабатыванию в дочерних? (наверное нет - с какого перепугу?).
Тогда я не понимаю почему всё так интересно у меня работает 8\
2 ION T © (28.05.02 20:29):
Эх... я об этом сразу подумала и обламилась... у меня что-то с делфями - InProc не отлаживаются по F7, F8 - почему не знаю 8(
2 Raptor © (29.05.02 00:03):
sorry, но 99% приведённого тобой кода - вода, связанная с MMF и механизмом межпроцессорной связи, которую я намеренно опустила. А вот это достаточно грубо, imho:
If Code IN [HC_ACTION, HC_NOREMOVE] Then
Begin
SendMessage(ShareInf^.AppWndHandle, WM_USER, ParamW, ParamL);
End;
Result := CallNextHookEx(ShareInf^.OldHookHandle, Code, ParamW, ParamL)
При каждом "мышином" событии отсылать WM_USER - ресурсов не жалко?
Но суть-то таже осталась, что у тебя, что у меня:
...
Case Message.wParam Of
WM_MOUSEMOVE:ms:="WM_MOUSEMOVE";
...
У меня тоже самое, но в callback ф-ии стоит 8(
Ладно, всем спасибо большое! Баг, как я поняла, в реализации, а не в подходе, - значит поборю сама...
Raptor (2002-05-29 00:57:00) [5]Если кому интересно, можете забрать откомментированые исходники вышеприведенного приме в кладовке.
http://delphi.mastak.ru/cgi-bin/download.pl?get=1022619349&n=1
Raptor (2002-05-29 00:03:00) [6]2 Fellomena
что if wParam= WM_RBUTTONDOWN then... - тут что-то не так.
Подскажите почему это не правильно, в help-е написанно, что в wParam у WH_MOUSE лежит именно Mouse event.
Или надо явно привести wParam к типу event? У меня ничего не вышло 8(
Не знаю почему у тебя не выходит. Должно работать. Может в самом деле проблема с MessageBox().
На всякий случай я приведу здесь код, который 100% работает у меня.
//************************* CUT HERE ***********************
DLL:
Library HookDLL;
Uses
Windows, Messages, SysUtils;
Const
GlobMapID = "Global Mouse Hook Demo {8C8BB695-606F-476A-9D9B-5B023C71EE7A}";
Type
PShareInf = ^TShareInf;
TShareInf = Record
AppWndHandle: HWND;
OldHookHandle: HHOOK;
hm:THandle;
End;
Var
MapHandle: THandle = 0;
ShareInf: PShareInf = nil;
ptr:PByteArray;
Procedure DLLEntryPoint(dwReason: DWORD); stdcall;
Begin
Case dwReason Of
DLL_PROCESS_ATTACH:
Begin
MapHandle:=CreateFileMapping(INVALID_HANDLE_VALUE, nil, PAGE_READWRITE, 0, SizeOf(TShareInf), GlobMapID);
ShareInf:=MapViewOfFile(MapHandle, FILE_MAP_ALL_ACCESS, 0, 0, SizeOf(TShareInf));
End;
DLL_PROCESS_DETACH:
Begin
UnMapViewOfFile(ShareInf);
CloseHandle(MapHandle);
End
End;
End;
Function MouseHook(Code: Integer; ParamW: WPARAM; ParamL: LPARAM): LRESULT;stdcall;
Begin
If Code IN [HC_ACTION, HC_NOREMOVE] Then
Begin
SendMessage(ShareInf^.AppWndHandle, WM_USER, ParamW, ParamL);
End;
Result := CallNextHookEx(ShareInf^.OldHookHandle, Code, ParamW, ParamL)
End;
Function SetMouseHook(Wnd: HWND): BOOL; stdcall;
Begin
If ShareInf<>Nil Then
Begin
ShareInf^.AppWndHandle:=Wnd;
ShareInf^.OldHookHandle:=SetWindowsHookEx(WH_MOUSE, @MOUSEHook, HInstance, 0);
Result:=ShareInf^.OldHookHandle<>0;
End
Else Result:=False
End;
Function RemoveMouseHook: BOOL; stdcall;
Begin
Result := UnhookWindowsHookEx(ShareInf^.OldHookHandle);
CloseHandle(ShareInf^.hm);
End;
Exports
SetMouseHook, RemoveMouseHook;
BEGIN
If DLLProc = Nil Then DLLProc := @DLLEntryPoint;
DLLEntryPoint(DLL_PROCESS_ATTACH);
END.
Main Application
Unit Main;
INTERFACE
Uses
Windows, Messages, Classes, Controls, Forms, StdCtrls, Buttons;
Type
TMainForm = Class(TForm)
ClearButton: TButton;
Memo1: TMemo;
BitBtn1: TBitBtn;
BitBtn2: TBitBtn;
Procedure ClearButtonClick(Sender: TObject);
Procedure BitBtn1Click(Sender: TObject);
Procedure BitBtn2Click(Sender: TObject);
Private
Procedure WMUser(Var Message: TMessage); Message WM_USER;
Protected
End;
Var
MainForm: TMainForm;
IMPLEMENTATION
Uses SysUtils;
{$R *.DFM}
Function SetMouseHook(Wnd: HWND): BOOL; stdcall; external "HookDLL.dll" name "SetMouseHook";
Function RemoveMouseHook: BOOL; stdcall; external "HookDLL.dll" name "RemoveMouseHook";
Procedure TMainForm.WMUser(var Message: TMessage);
Var MHS:PMouseHookStruct;
ms,rs:String;
Begin
Case Message.wParam Of
WM_MOUSEMOVE:ms:="WM_MOUSEMOVE";
WM_LBUTTONUP:ms:="WM_LBUTTONUP";
WM_LBUTTONDOWN:ms:="WM_LBUTTONDOWN";
WM_LBUTTONDBLCLK:ms:="WM_LBUTTONDBLCLK";
WM_RBUTTONUP:ms:="WM_RBUTTONUP";
WM_RBUTTONDOWN:ms:="WM_RBUTTONDOWN";
WM_RBUTTONDBLCLK:ms:="WM_RBUTTONDBLCLK";
WM_MBUTTONUP:ms:="WM_MBUTTONUP";
WM_MBUTTONDOWN:ms:="WM_MBUTTONDOWN";
WM_MBUTTONDBLCLK:ms:="WM_MBUTTONDBLCLK";
WM_MOUSEWHEEL:ms:="WM_MOUSEWHEEL";
WM_NCMOUSEMOVE:ms:="WM_NCMOUSEMOVE";
End;
rs:="Message: "+ms;
Memo1.Lines.Add(rs);
End;
Procedure TMainForm.ClearButtonClick(Sender: TObject);
Begin
Memo1.Lines.Clear;
End;
Procedure TMainForm.BitBtn1Click(Sender: TObject);
Begin
If NOT SetMouseHook(Handle) Then
MessageBox(Handle, "Unable to set hook", PChar(Application.Title), MB_OK OR MB_ICONHAND);
End;
Procedure TMainForm.BitBtn2Click(Sender: TObject);
Begin
If NOT RemoveMouseHook Then
MessageBox(Handle, "Unable to remove hook", PChar(Application.Title), MB_OK OR MB_ICONHAND);
End;
End.
//************************* CUT HERE ***********************
Думаю форму ты и сама сможешь реинкарнировать по этому коду. ;-))
ION T (2002-05-28 20:29:00) [7]По-моему легче поставить брейкпоинт на строку
if code= HC_ACTION then, приаттачить длл-ку к любому использующему её процессу и ждать.....:)) Ну а там Ctrl+Click на переменных, Ф8 и прочие радости дебаггера......
Digitman (2002-05-28 18:25:00) [8]На основании чего ты утверждаешь, что тело MouseProc() не выполняется ? Лишь на основании того, что не видишь окна, ожидаемого по MessageBox(..) ?
Убедись в том , что callback вызывается вообще. Выведи безусловно в MessageBox() содержимое wParam, сравни значение с прототипом.
Убедись в том, что вызов MessageBox() происходит безопасно : callback может вызываться и не в осн.потоке контролирующего процесса
Fellomena (2002-05-28 17:53:00) [9]ok, thanks ребята!!! Ясно вроде всё 8)
С бонусом разделались.
Что насчёт WH_MOUSEи его использования?
Виктор Щербаков (2002-05-28 11:56:00) [10]
> Хм... но ведь компилятор должен знать, что возвращает callback
> функция при использовании WH_MOUSE. Или нет?
Он знает конечно :), но только то, что lParam - это просто longint. Все остальные манипуляции - задача программиста.
Лично я всегда ставлю себя на место компилятора. После этого сразу понятно где и какие преобразования нужны. Попробуй и ты :)
Digitman (2002-05-28 11:55:00) [11]>>BONUS:
> LoadLibrary("MyDll.dll"); и LoadLibrary(PChar("MyDll.dll"));
Для компилятора - абсолютно индифферентно.
>X:= TMsg(Pointer(lParam)^).pt.X
компилятором интерпретируется так :
- взять lparam как DWORD;
- рассматривать его как POINTER (указатель);
- то, на что указывает содержимое указателя, рассматривать как адрес структуры TMsg (разыменовать указатель);
- и т.д. ... (работа собственно со структурой по известным на момент компиляции смещениям ее именованых полей)
А вот здесь для компилятора - нонсенс :
x:= lParam^.pt.X;
- взять lparam как DWORD;
- ??? это не указатель, а требуется его разыменование !!
- даже если предположить, что lparam - указатель, непонятно, адрес чего будет представлять его содержимое после предположительно возможного разыменования
- ??? что есть pt ? что есть pt.X ???? тобой как бы ожидается, что компилятор использует эти идентификаторы как имена полей в некоей структуре для расчета отн.смещений этих полей. Но - какой конкретно структуры ? Это как раз и непонятно. Результат - отказ компиляции по ошибке "неизвестный идентификатор"
Fellomena (2002-05-28 11:49:00) [12]2 Виктор Щербаков © (28.05.02 11:40):
>> Param^ - Это то, что находится в памяяти по адресу lParam. .pt.X - Это обращение к члену структуры.
Это-то без объяснений понятно, а вот:
>> Для того, чтобы компилятор, мог сгенерировать код, необходимый для такого обращения, ему нужно знать, что за структура имеется ввиду...
Хм... но ведь компилятор должензнать, чтовозвращает callback функция при использовании WH_MOUSE. Или нет?
...хотя,... действительно, к примеру, когда мы используем тот же ToolHelp мы в разделе var явно определяем тип структуры в которой расположена интресующая нас инфа.
Т.е. я делаю вывод, что те структуры, которые не входят в библиотеки Delphi, к примеру те, что возвращают некоторые API ф-ии, всегда должны быть явно приведены.
Так?
Thanks for answer :)
Виктор Щербаков (2002-05-28 11:40:00) [13]lParam^ - Это то, что находится в памяяти по адресу lParam.
.pt.X - Это обращение к члену структуры.
Для того, чтобы компилятор, мог сгенерировать код, необходимый для такого обращения, ему нужно знать, что за структура имеется ввиду (её размер, поля и т.д.), т.е. производя приведение к типу, мы как бы говорим компилятору: "Область памяти, на сомом деле является структурой такого-то типа." После чего компилятор может попытаться сгенерировать соответствующий код.
Строковые же константы преобразуются к указателям PChar.
Такие преобразования по умолчаиню выполняются еще в ряде случаев. В каких точно - где-то читал и уже не помню. Нужно просто почитать докумментацию по Object Pascal. Там точно есть.
И естественно, всегда нужно преобразовывать целые числа к указателям на "самопальные" структуры данных явно.