Checking for activity on a form

Hi,

I wish to check for activity in the form of mouse moves or keyboard activity on a form. The form is rather busy in that it has many controls, panels etc on it.

What I wish to know is, is there a generic way of detecting activity on every control on the form without setting the OnMouseMove and OnKeyDown for every control on the form?

Chris
ChrisBerryAsked:
Who is Participating?
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.

rwilson032697Commented:
It is easy enough to do for key presses. Just provide an OnKeyPress etc handler and set KeyPreview to be true.

Not sure about the mouse moving though, other than setting the same mouse move events for every control (easy enough, jsut select them all and set the appropriate event in the object inspector).

Cheers,

Raymond.
0
LischkeCommented:
An quite unusal yet very effectiv way of doing this is to hook into the main message loop by calling:

  HookMainWindow

passing a function name which can accept a message record. In this function you would then check for mouse and keyboard messages like:

function ActivityCheck(var Message: TMessage): Boolean;

begin
  // we don't want to swallow messages, but just examine
  Result := False;
  if (Screen.ActiveForm = FormToWatch) and
    (Message.Msg in [WM_KEYFRIST..WM_KEYLAST, WM_MOSUEFIRST..WM_MOUSELAST]) then
    // form got some input, act accordingly
end;

Ciao, Mike
0
ChrisBerryAuthor Commented:
Mike,

Looks like what I am after, however could you please expand on the use of HookMainWindow as I can find nothing like this either in Delphi or the Windows API help. BTW I am using Delphi 3.

Cheers

Chris
0
Cloud Class® Course: CompTIA Cloud+

The CompTIA Cloud+ Basic training course will teach you about cloud concepts and models, data storage, networking, and network infrastructure.

LischkeCommented:
Chris,

you should try a bit harder. In your Delphi help go to the Index page and type in HookMainWindow and you will be presented the description for TApplication.HookMainWindow. The description is somewhat stupid, but nonetheless an indication where to go.

Ciao, Mike
0
ChrisBerryAuthor Commented:
Sorry, can't think what I did not to get the help topic.

Will explore and let you know.

Thanks

Chris
0
LischkeCommented:
In the case you don't find the description, here's the one from Delphi 5 which is more or less the same as the one in D3:

Enables a non-VCL dialog box to receive messages sent to the application’s main window.

procedure HookMainWindow(Hook: TWindowHook);

Description

Use HookMainWindow to ensure that a non-VCL dialog box behaves correctly as a child of the application, not as a standalone window. For example, switching among applications with Alt+Tab treats the application as a single task after calling HookMainWindow, rather than treating the non-VCL dialog box as a separate task.

When the window identified by the Handle property receives relevant dialog messages, it passes them to the dialog procedure passed as the Hook parameter.

There is no problem with leaving a dialog box hooked into the main window, even for extended periods. However, should the dialog box close, call the UnhookMainWindow method to release the hook.

If you have the VCL sources then I would recommend that you look up TApplication.

Ciao, Mike
0
ChrisBerryAuthor Commented:
Hi Mike,

Yes I did find the description but I now have problems with the example you have given me.

Using the Message.msg in [xx..yy, aa..bb] I get a compile error telling me I have subrange errors. If I use Message.Msg = WM_xxx I seem to get random messages passed to the function passed in the HookMainWindow.

Just to give you further information on what I am trying to achieve.

I wish to logoff a user from the application if and only if the main form of the application is open and no activity has taken place on the form for a defined period.

I could use the OnMouseMove and OnKeyDown of every control on the form however this conflicts with some existing functions and some controls do not have the On.. events.

Regards

Chris
0
LischkeCommented:
Chris, as you can see in the given sample I wrote it form memory not taking into account possible subrange errors. You can easily rewrite this as:

  with Message do
    if (Msg >= WM_MOUSEFIRST) and (Msg <= WM_MOUSELAST) or
       (Msg >= WM_KEYFIRST) and (Msg <= WM_MOUSELAST) then ...

Ciao, Mike
0
gandalf_the_whiteCommented:
listening...
0
ChrisBerryAuthor Commented:
Hi Mike,

Yes I had tried something like that but...

I do not know where to go from here.
I have put a listbox in the ActivityCheck function to log the messages being received (no range checking etc) and am only receiving a limited number of messages such as clicking on the title bar of the form or inputing text into an edit box. No mouse move or general key presses.
0
she3i3iCommented:
Chris,

   I also need what you just asked for for a client/server app I'm currently writing where I need to disconnect idle clients and so I've written this for you (tested and works great):

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
    procedure WndProc(var Message : TMessage); override;
    procedure Reset;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  Timeout: Integer;

implementation

{$R *.DFM}

procedure TForm1.WndProc(var Message : TMessage);
begin
   if (Message.Msg = CM_MOUSEENTER)
      or (Message.Msg = CM_MOUSELEAVE)
      or (Message.Msg = WM_KEYDOWN)
      // add as many messages as you like.
   then Reset;
   inherited WndProc(Message);
end;

procedure TForm1.Reset;
begin
      Timeout := 0; // activity detected, reset idle
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
   Inc(Timeout); // increment since idle
   if Timeout = 10 then Close; // logoff user here (where 10 = seconds)
end;

end.


Regards,

Chris.
0
LischkeCommented:
Thank you Chris (she3i3i), very polite, thank you (I hate it when this happens. You have seen we already discussed here for a while now but you proposed an answer.)

Chris (ChrisBerry),

well, I just checked what you wrote above and to my surprise you are right. This is a new fact for me that not all messages go through the default window procedure (as I assume before) and it makes sense if you think about it (performance). Anyway, this leaves us with the question what else we can do. She3i3i solution is nonsense as it only captures mouse enter and mouse leave messages (which are btw. quite unreliable) and key downs, but not mouse move etc. Additionally, the messages are of course only for the main form. If you enter something in an edit box you will see that there's no message for the form (the problem is very similar to my suggestion to hook the entire application).

For the key message we can use the built-in feature KeyPreview. If this property is set to true then the form's key down event handler is called for every key down message even if it is for another window.

For the mouse messages this becomes more difficult. The only idea I have here is to use the timer you have to setup anyway to check for timeout to call GetCursorPos and compare it to a previously stored position to know whether the mouse position changed. You need to handle the case where the mouse is not over one of your windows (form, edits, memos etc.). You can do this by using FindVCLWindow. If no control is returned than handle this as would the mouse have not moved. This way you get the timeout even if the user work with another application.

Ciao, Mike
0

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
she3i3iCommented:
Quote:
"(which are btw. quite unreliable) and key downs, but not mouse move etc."

well...

if (Message.Msg = WM_MOUSEMOVE)

works fine for me!

Oh well...
0
ChrisBerryAuthor Commented:
I see my final comment never made it.

Chris you are correct in that MouseMove is detected, however Mike you are also correct that it is only in the main window and not over the controls on the form.

After some experimentation I have decided to go with the GetCursorPos solution using Screen.ActiveForm as well.

Thanks for all your input.

Regards

Chris
0
LischkeCommented:
Yup, always glad to help :-)

Ciao, Mike
0
she3i3iCommented:
Chris,

   Because I'm not really the big bad wolf Mike has tried to make me out to be, I'm sharing this with you...

private
   procedure AppMessage(var Msg: TMsg; var Handled: Boolean);

var
   Form1: TForm1;
   IdleTimer : Integer; // global variable

procedure TForm1.FormCreate(Sender: TObject);
begin
   IdleTimer := 0;
   Application.OnMessage := AppMessage;
end;

procedure TForm1.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
   if (Msg.Message = WM_MOUSEMOVE)
   or (Msg.Message = WM_KEYUP) then begin
      IdleTimer := 0; // mouse move or key press detected
      Handled := True;
   end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
   Inc(IdleTimer);
   if IdleTimer = 10 then Close; // log off your user here
end;




This works on any part of anything within my application. Hope it helps you!


Regards, Chris.
0
ChrisBerryAuthor Commented:
Hi Chris,

You are quite right, it works great. Thanks for your input.

Regards

Chris
0
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
Delphi

From novice to tech pro — start learning today.