Link to home
Start Free TrialLog in
Avatar of nem2k4
nem2k4

asked on

Hooking mouse movement

This is a followup to this question:

https://www.experts-exchange.com/questions/21116276/Mouse-click-key-press-logging.html

I have modified the library to also track mouse movement, so that (useless? ;)) numbers like mouse distance travelled, current mouse position etc. can be obtained.

The mouse hook procedure now looks like this:


// ======================================================
function MouseHookFunc(Code, wParam, MouseStrut: Integer): Integer; stdcall;
var
    ms:PMouseHookStruct;
    screen:HDC;
begin
    {this is the Mouse Hook function, I call the CallNextHookEx first because
    you do not need to block the Mouse events}
    Result := CallNextHookEx(0, Code, wParam, MouseStrut);

    if Code < 0 then Exit;

    if (Code = HC_ACTION) then begin
        {to make sure I get a Click and not a drag, I will get the Ticks in DTime}
        {if (wParam = WM_RBUTTONDOWN) or (wParam = WM_LBUTTONDOWN) or (wParam = WM_MBUTTONDOWN) then
            DTime := GetTickCount;}

        if (wParam = WM_LBUTTONUP) {and (GetTickCount - DTime < 400)} and
        not IsBadCodePtr(PHookRec1) then
            if PHookRec1.LeftBCount < MAXDWORD then  // do not go over range
                Inc(PHookRec1.LeftBCount);

        if (wParam = WM_RBUTTONUP) {and (GetTickCount - DTime < 400)} and
        not IsBadCodePtr(PHookRec1) then
            if PHookRec1.RightBCount < MAXDWORD then  // do not go over range
                Inc(PHookRec1.RightBCount);

        if (wParam = WM_MBUTTONUP) {and (GetTickCount - DTime < 400)} and
        not IsBadCodePtr(PHookRec1) then
            if PHookRec1.MiddleBCount < MAXDWORD then  // do not go over range
                Inc(PHookRec1.MiddleBCount);

        if (wParam = WM_MOUSEMOVE) and
        not IsBadCodePtr(PHookRec1) then begin

            ms := PMouseHookStruct(MouseStrut);

            // pixels
            PHookRec1.MouseDistance := PHookRec1.MouseDistance +
                sqrt( power(ms^.pt.X - PHookRec1.LastMouseX,2) + power(ms^.pt.Y - PHookRec1.LastMouseY,2)   );

            // millimeters
            screen := getdc(0);
            PHookRec1.MouseDistanceMM := PHookRec1.MouseDistanceMM +
                sqrt    (   power( (ms^.pt.X - PHookRec1.LastMouseX)*getdevicecaps(screen,HORZSIZE)/getdevicecaps(screen,HORZRES) ,2) +
                            power( (ms^.pt.Y - PHookRec1.LastMouseY)*getdevicecaps(screen,VERTSIZE)/getdevicecaps(screen,VERTRES) ,2)
                        );



            PHookRec1.LastMouseX := ms^.pt.X;
            PHookRec1.LastMouseY := ms^.pt.Y;

        end;


    end;
end;
// ======================================================

This is working great most of the time, except when I move my mouse very fast. eg. if I start my mouse in the upper-left corner of the screen and move it very quickly to the bottom-right corner, the reported Y-coordinate of the mouse is less than 1024 (my screen height).

Are there timing issues involved here? Is my MouseProc too complex and it is having trouble keeping up with the large number of mouse messages it has to process?
ASKER CERTIFIED SOLUTION
Avatar of Member_2_248744
Member_2_248744
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of nem2k4
nem2k4

ASKER

What about using a LowLevelMouseProc? Would that be more likely to be able to catch all mouse movement? I suppose I could always use GetCursorPos to get the mouse position but that might make things even slower or not help at all...

Are you sure getdevicecaps(screen,HORZSIZE) does not give correct (or "near-enough" - it doesn't have to be perfect) numbers for monitors? It gave the same result on 2 different 19" monitors, and slightly lower for my laptop screen (not sure how big it is, maybe 15" LCD?), I don't have a ruler here at work to measure the screens but I'll bring one in tomorrow to measure with.
I think Slick812  is right about his comments, you can also change the mouse sensitivity in the control panel | mouse, make it go slower and you'll be able to capture all the movements... but you probably don't want that
???
I have never used (or did any information research) about  "LowLevelMouseProc"  (whatever that may mean), I guess you want to some how, get a mouse driver event (mouse move) at a more responsive (lower lever) system function, but I have never wanted, ,  or tried to do that, so I can not say anything about that, I feel lucky if I can get a system cursor movement Hook at all. . . .
You could write your own mouse driver I guess?, but I do not know enough about that sort of thing, to say if you can get what you want or not from doing your own mouse driver (It is well reported that Delphi can not do hardware drivers). . ,

as to the  getdevicecaps(screen,HORZSIZE), , I remember trying that years ago, and it did not work at all, but with the newer win system ( Win 2000 or Win XP), it may work now? if you try it and it works for you, then I was wrong, sorry

as far as the   MouseHookFunc   above, as far as I know, if you get the mouse move message, you get it, , , and if you do not, then you do not get it, I know of NOTHING to try to do to change it. . . . .
Oh, I do not think that you could call  GetCursorPos( ), when would you call that? and How? in a Timer event?
if you call it in the  WM_MOUSEMOVE then it should just give your the same position, I do not beleive it would give you any "Missed" mouse messages.
Avatar of nem2k4

ASKER

LowLevelMouseProc: http://msdn.microsoft.com/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/lowlevelmouseproc.asp?frame=true

According to MSDN, MouseProc:
"The MouseProc hook procedure is an application-defined or library-defined callback function used with the SetWindowsHookEx function. The system calls this function whenever an application calls the GetMessage or PeekMessage function and there is a mouse message to be processed."

And LowLevelMouseProc:
"The LowLevelMouseProc hook procedure is an application-defined or library-defined callback function used with the SetWindowsHookEx function. The system call this function every time a new mouse input event is about to be posted into a thread input queue. The mouse input can come from the local mouse driver or from calls to the mouse_event function. If the input comes from a call to mouse_event, the input was "injected". However, the WH_MOUSE_LL hook is not injected into another process. Instead, the context switches back to the process that installed the hook and it is called in its original context. Then the context switches back to the application that generated the event. "

Perhaps the problem is that the application calls GetMessage but there's no mouse input to process when you move the mouse very fast (GetMessage might be called on a timer basis?)

You're probably right about the GetCursorPos thing :)
I really have no Idea whatsoever about -
" there's no mouse input to process when you move the mouse very fast"

I have not used the  WH_MOUSE_LL   hook type, so I can not comment on it, I did read some of the MSDN web page that you gave the addy for, I could not see much in their description that would help you for this. . . it seemed like the WH_MOUSE_LL hook type would also get the  mouse_event  along with the normal mouse events, but I could not get anything as to MORE events or faster events? but thats just my first look, who knows, it may do just what you need?

also, , , I do not see how you are having problems, now that I have thought about it more. . .  even if it would "Skip" a move message the code has

PHookRec1.LastMPt := MouseStrut.pt;

which will remember the last point that it did get, and process the distance for that last point, reguardless of any "Skipped" messages. . .

are you sure about your processing distance code?. . . but this is mostly just head analisis, so it may not have any value

have you tried my code without the MouseDistanceMM  calculations (which may be a time comsumer and block some message)

sorry I do not have a soulution, but I have no ideas for a fix