Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

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. Там точно есть.
И естественно, всегда нужно преобразовывать целые числа к указателям на "самопальные" структуры данных явно.


Наверх

Титульная страница Поиск, карта сайта Написать письмо