Hooking mouse movement

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?
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

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}

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
nem2k4Author Commented:
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
Become a CompTIA Certified Healthcare IT Tech

This course will help prep you to earn the CompTIA Healthcare IT Technician certification showing that you have the knowledge and skills needed to succeed in installing, managing, and troubleshooting IT systems in medical and clinical settings.

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.
nem2k4Author Commented:
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
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today

From novice to tech pro — start learning today.