Форум: "WinAPI";
Текущий архив: 2002.08.08;
Скачать: [xml.tar.bz2];
ВнизWH_MOUSE - troubleshooting + BONUS (Вопрос о приведении типов). Найти похожие ветки
← →
Fellomena (2002-05-28 11:16) [0]Всем привет! Раньше, когда надо было отлавливать события мыши я не
мучаясь ставила 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;
то работать не будет.
Как определить как в данном случае правильно?
← →
Виктор Щербаков (2002-05-28 11:40) [1]lParam^ - Это то, что находится в памяяти по адресу lParam.
.pt.X - Это обращение к члену структуры.
Для того, чтобы компилятор, мог сгенерировать код, необходимый для такого обращения, ему нужно знать, что за структура имеется ввиду (её размер, поля и т.д.), т.е. производя приведение к типу, мы как бы говорим компилятору: "Область памяти, на сомом деле является структурой такого-то типа." После чего компилятор может попытаться сгенерировать соответствующий код.
Строковые же константы преобразуются к указателям PChar.
Такие преобразования по умолчаиню выполняются еще в ряде случаев. В каких точно - где-то читал и уже не помню. Нужно просто почитать докумментацию по Object Pascal. Там точно есть.
И естественно, всегда нужно преобразовывать целые числа к указателям на "самопальные" структуры данных явно.
← →
Fellomena (2002-05-28 11:49) [2]2 Виктор Щербаков © (28.05.02 11:40):
>> Param^ - Это то, что находится в памяяти по адресу lParam. .pt.X - Это обращение к члену структуры.
Это-то без объяснений понятно, а вот:
>> Для того, чтобы компилятор, мог сгенерировать код, необходимый для такого обращения, ему нужно знать, что за структура имеется ввиду...
Хм... но ведь компилятор должен знать, что возвращает callback функция при использовании WH_MOUSE. Или нет?
...хотя,... действительно, к примеру, когда мы используем тот же ToolHelp мы в разделе var явно определяем тип структуры в которой расположена интресующая нас инфа.
Т.е. я делаю вывод, что те структуры, которые не входят в библиотеки Delphi, к примеру те, что возвращают некоторые API ф-ии, всегда должны быть явно приведены.
Так?
Thanks for answer :)
← →
Digitman (2002-05-28 11:55) [3]>>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 ???? тобой как бы ожидается, что компилятор использует эти идентификаторы как имена полей в некоей структуре для расчета отн.смещений этих полей. Но - какой конкретно структуры ? Это как раз и непонятно. Результат - отказ компиляции по ошибке "неизвестный идентификатор"
← →
Виктор Щербаков (2002-05-28 11:56) [4]
> Хм... но ведь компилятор должен знать, что возвращает callback
> функция при использовании WH_MOUSE. Или нет?
Он знает конечно :), но только то, что lParam - это просто longint. Все остальные манипуляции - задача программиста.
Лично я всегда ставлю себя на место компилятора. После этого сразу понятно где и какие преобразования нужны. Попробуй и ты :)
← →
Fellomena (2002-05-28 17:53) [5]ok, thanks ребята!!! Ясно вроде всё 8)
С бонусом разделались.
Что насчёт WH_MOUSE и его использования?
← →
Digitman (2002-05-28 18:25) [6]На основании чего ты утверждаешь, что тело MouseProc() не выполняется ? Лишь на основании того, что не видишь окна, ожидаемого по MessageBox(..) ?
Убедись в том , что callback вызывается вообще. Выведи безусловно в MessageBox() содержимое wParam, сравни значение с прототипом.
Убедись в том, что вызов MessageBox() происходит безопасно : callback может вызываться и не в осн.потоке контролирующего процесса
← →
ION T (2002-05-28 20:29) [7]По-моему легче поставить брейкпоинт на строку
if code= HC_ACTION then
, приаттачить длл-ку к любому использующему её процессу и ждать.....:)) Ну а там Ctrl+Click на переменных, Ф8 и прочие радости дебаггера......
← →
Raptor (2002-05-29 00:03) [8]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 ***********************
Думаю форму ты и сама сможешь реинкарнировать по этому коду. ;-))
← →
Raptor (2002-05-29 00:57) [9]Если кому интересно, можете забрать откомментированые исходники вышеприведенного приме в кладовке.
http://delphi.mastak.ru/cgi-bin/download.pl?get=1022619349&n=1
← →
Fellomena (2002-05-29 12:09) [10]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(
Ладно, всем спасибо большое! Баг, как я поняла, в реализации, а не в подходе, - значит поборю сама...
← →
Digitman (2002-05-29 13:14) [11]>>"..но не работает"
Это можно трактовать как угодно, в.т. и невыполнение тела callback вообще.
Разумеется, ты получишь кучу MessageBox() - ты же никак не анализируешь источник события нажатия клавиши мыши (в дан.случае - клик в зоне активного окна IE), а просто фиксируешь сам факт его возникновения (вернее - его постановку в очереди сообщений окон каждого из активных процессов) выбросом MessageBox()
Проецирование чего-то там на поток - это нонсенс. Проецирование кодового сегмента DLL на вирт.АП процесса - это другое дело. В твоем случае будет создана проекция образа DLL на АП хост-процесса, коим будет являться процесс, выполняющий целевую загрузку этой DLL (т.е. процесс, который планирует вызов ф-ции SetHook). Весь кодовый сегмент DLL, в т.ч. и фрагмент с телом callback-ф-ции, будет спроецирован на АП этого процесса. В АП прочих активных процессов в системе никаких преобразований не происходит : диспетчеризацией и вызовами callback-ф-ции будет управлять сама система - только она знает, в АП какого конкретно процесса расположена точка входа в ф-цию
← →
Raptor (2002-05-30 17:31) [12]2 Fellomena (29.05.02 12:09)
> 2 Raptor © (29.05.02 00:03):
> sorry, но 99% приведённого тобой кода - вода, связанная
> с MMF и механизмом межпроцессорной связи, которую я намеренно
> опустила. А вот это достаточно грубо, imho:
>
> При каждом "мышином" событии отсылать WM_USER - ресурсов не
> жалко?
Возможно ты и права. Но у меня эта "вода" работает как на моей Win2k так и на рядом стоящей Win98. А у тебя, судя по всему, нет. ;-))
Посему советую не полагаться на MS и на ее исправления, а воспользоваться старым и проверенным способом, тоесть использовать MMF.
А насчет грубости, так это же только демонстрация, которая наглядно показывает, что все работает нормально. Если не нравится можно не отсылать сообщения, а обрабатывать все прямо в хуке, это не принципиально.
← →
Fellomena (2002-05-31 15:56) [13]2 Raptor:
>> "Если не нравится можно не отсылать сообщения, а обрабатывать все прямо в хуке, это не принципиально."
Хех 8))) В том-то и дело - не получается прямо в хуке и всё тут 8)))
А против MMF я ничего против не имею и тоже его использую как в Win9x так и в Win2k.
Вот кончатся экзамены - разберусь я с этим WH_MOUSE как пить дать.
Страницы: 1 вся ветка
Форум: "WinAPI";
Текущий архив: 2002.08.08;
Скачать: [xml.tar.bz2];
Память: 0.52 MB
Время: 0.007 c