Expiring Today—Celebrate National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17


Hooking mouse movement

Posted on 2004-09-06
Medium Priority
Last Modified: 2010-05-18
This is a followup to this question:


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;
    {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

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

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

        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;


// ======================================================

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?
Question by:nem2k4
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 2
LVL 34

Accepted Solution

Slick812 earned 150 total points
ID: 11993786
I can not do any testing to see if this is a problem or not, but I think the mouse movement messages are not suppose to really capture "All" of the mouse movements, just enough to show a resonable cursor tracking on your monitor, There is some kind of setup in the mouse driver, that will "Get" or "Capture" a mouse "Movement" at certain intervals, or maybe there's a pixel distance factor there also (you can also set your cursor movement speed to vary). So if you move your mouse very rapidly, either the mouse driver will miss some of the movements or some other system factor, will just skip the movement "Capture" to where the mouse singnals are for the last movememt sample. . .
You do alot of processing of distance, and this also may have some effect (I doubt it if your machine is faster than 2 Gig Hertz), , , So there may not be a cure for your problem, at least I do not know of one.
Also you are trying to get the millimeter movement, but your method WILL NOT WORK, , you are using the getdevicecaps(screen,HORZSIZE), this WILL NOT give you the correct size of the Monitor, the  getdevicecaps(screen,HORZSIZE)  is useful for printers, not for monitors, so you can leave out all of the code for the millimeter movement. . .

  PHookRec = ^THookRec;
  THookRec = packed Record
    KeyCount, LeftBCount, RightBCount, MiddleBCount,
    MouseDistance: Cardinal;
    LastMPt: TPoint;

function MouseHookFunc(Code, wParam: Integer; var MouseStrut: TMOUSEHOOKSTRUCT): Integer; stdcall;
dist1: Cardinal;
Result := CallNextHookEx(0, Code, wParam, Integer(@MouseStrut));
if (Code < 0) or IsBadCodePtr(PHookRec1) then Exit;

if (Code = HC_ACTION)then
  if wParam = WM_LBUTTONUP then
    if PHookRec1.LeftBCount < MAXDWORD then  // do not go over range

  if wParam = WM_RBUTTONUP then
    if PHookRec1.RightBCount < MAXDWORD then  // do not go over range

  if wParam = WM_MBUTTONUP then
    if PHookRec1.MiddleBCount < MAXDWORD then  // do not go over range

  if wParam = WM_MOUSEMOVE then
    dist1 := Round(Sqrt(SumOfSquares([abs(MouseStrut.pt.y-PHookRec1.LastMPt.y),
    if PHookRec1.MouseDistance < MAXDWORD-dist1 then  // do not go over range
        PHookRec1.MouseDistance := PHookRec1.MouseDistance+ dist1;
    PHookRec1.LastMPt := MouseStrut.pt;

 - - - - - - - - - - - - -  - - - - - -  - - - - - - - - - - - - - - - - - - - - -

and if you want to get the distance in Millimeters or inches, then you will need to Measure your monitor and do the math for the distance

Label2.Caption := IntTostr(Round(PHookRec1.MouseDistance/3.555))+' Millimeters';
 {I measured my monitor screen and it was 324 millimeters wide
 resolution was 1152x864 so 1152 / 324 is 3.555}

Author Comment

ID: 11993861
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.
LVL 13

Expert Comment

ID: 11999259
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
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

LVL 34

Expert Comment

ID: 11999507
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. . . . .
LVL 34

Expert Comment

ID: 11999577
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.

Author Comment

ID: 12000686
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 :)
LVL 34

Expert Comment

ID: 12002273
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

Featured Post

New feature and membership benefit!

New feature! Upgrade and increase expert visibility of your issues with Priority Questions.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
This is my first video review of Microsoft Bookings, I will be doing a part two with a bit more information, but wanted to get this out to you folks.
Want to learn how to record your desktop screen without having to use an outside camera. Click on this video and learn how to use the cool google extension called "Screencastify"! Step 1: Open a new google tab Step 2: Go to the left hand upper corn…
Suggested Courses

719 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question