Link to home
Start Free TrialLog in
Avatar of Dark_King
Dark_King

asked on

Detect user inactivity

Detect user inactivity

I need to make a consol application thats run a aplication if user is inactiv.
How sholde I scan for inactivity in windows.


Is it possible to hav it use same function as this if user is inactive for some time.
Exempel: Inactivity.exe -20
After 20 sec ut lock down the workstation.

%windir%\system32\rundll32.exe user32.dll LockWorkStation
Avatar of Peter_
Peter_

I used this trick sometime back when needing to know if the user had been inactive.

Set the windows screensaver timeout (Screen properties/ screen saver/ wait ? minutes) to the requested value. This can also be done using code.

When user has been inactive for this amount of time, the SC_SCREENSAVE message is sent systemwide (once every 30 seconds or so, until user is no longer inactive). The good thing is that this happens regardless of if a sceensaver has actually been choosen or not (in screen properties/ screen saver). So even if a screensaver has not been started, this indicates that it should have been if selected.

I had an application always running that looked for this message. I also added a timer as to do the action just once (not every time the message is caught).

procedure TForm1.ResetInactive(var Msg: TMsg; var Handled: boolean);
begin
  if (Msg.message = WM_SYSCOMMAND) then
  begin
   if (Msg.wParam = SC_SCREENSAVE) then
   begin
    if (not SystemInactive) then
    begin
     // Do something... User is inactive!
     IdleTimer.Enabled:=True;
   end;
   SystemInactive:=True;
   FLastActive := Now;
end;

procedure TForm1.IdleTimerTimer(Sender: TObject);
begin
 if ((FLastActive + EncodeTime( 0, 0, 30, 0 )) < Now) then
 begin
  SystemInactive:=False;
  IdleTimer.Enabled:=False;
 end;
end;

This worked great using windows 2000.
Dark_King,
  Here's a comment I had posted recently: essentially, I hooked into keyboard and mouse events and monitored how long thy were inactive.
  You could use a journal record hook in your application, reset the timer on any keyboard or mouse event and set the timerinterval to whatever parameter you receive.
  In the ontimer, you could write the code to do whatever you want. I hope this makes some sense.

Cheers!
...Shu
  Here's my comment from another question:
(https://www.experts-exchange.com/questions/20807081/After-an-idle-period-shutdown-a-windows-machine-as-NT-service.html)
  I am working on a utility that monitoris the Idle time of the PC. The way I am doing it is to hook to keyboard and mouse events to find out how long the system was idle (using a service). I check it periodically (using a timer) and save the idle time.
  If that helps, read on...
  I have asked a few questions at EE for doing that and you could have a look at those. There are two ways you could do it:
1) Using Journal hooks
2) Using individual Keyboard/mouse hooks
  When I asked here, I was recommended that for longer periods of time, non-journal hooks are better. But perhaps journal hooks are simple. So, you could decide on what you want.

Links:
  My journal hook service code is posted in this Q:
https://www.experts-exchange.com/questions/20794138/Does-JournalRecord-hook-from-service-require-Allow-service-to-interact-With-Desktop.html

This is where I got the code for a DLL that creates Kbd hooks (non-journal):
http://www.swissdelphicenter.ch/torry/showcode.php?id=1722

This is for using the DLL described above from the service:
https://www.experts-exchange.com/questions/20750612/Security-Attributes-in-CreateFileMapping.html

And this is the debate of what to use journal or other hook:
https://www.experts-exchange.com/questions/20750833/Difference-between-JournalRecord-and-Other-hooks.html

  I am asking you to chase a lot of links, but doing that gave me a better understanding of what I wanted. hope it works the same way for you too.
  Cheers,
...Snehanshu
Avatar of Dark_King

ASKER

OK

DLL service for hook, it sounds right watt I need.

Is the cod on
https://www.experts-exchange.com/questions/20750612/Security-Attributes-in-CreateFileMapping.html

complete and fix this service, I not that god at Delphi and have never make a Service.
If it makes me a .dll file how do I install this as service.

And my program that’s call this service most be "console" type, so I can’t
use any Forms and visual components.

I can raze point to get more help,  I not that good in Delphi….

I need to run this program on XP and Win2000, it’s in school for teacher
that forget do logout, and make this a service keep student from exit this App.
Dark_King,
  I have just returned after a weekend trip: I shall provide a better comment tomorrow.
  For now, here are a few quick comments:
1) No, the link you mentioned doesn't have a complete solution.
2) If you don't want, you may not use a service, the hook will work even with a normal application.
3) You don't have to "install the DLL as a service": you have to use the DLL from some application or a service (how-tos later).
4) Regarding your comment:
>>And my program that’s call this service most be "console" type, so I can’t
>>use any Forms and visual components.

 This is tricky: you want a Console App! Why? are you running in DOS? BTW, you can make a service (which would have no forms etc.) or a formless application.

5) I would have commented and tried to help even if your question carried fewer points. So, if you do get a solution, and if YOU feel the help you received was worth more, you could post another question for extra points.
see
https://www.experts-exchange.com/help.jsp#hi50

That's it for now. more later.
Cheers!
...Shu
Dark_King,
  I was waiting for your feedback. ne way, I think you can start with this simple app I am pasting here so that you are familiar with what is required.
  If after this, you still want to implement this as a service, then let me know.
  The code I am pasting is a form with two buttons and a timer. Button1 starts detecting the inactivity, buton2 stops.
  The timer is to keep track of idle time. The application accepts arguments like
InactMon.exe 100
which means that it will scan for 100 seconds of inactivity.
  Try using this and let me know if you have any questions. This approach requires no DLLs or Service.
  Also, note that you could start the hook in formcreate and stop it in formclose in case you do not want it to be done by buttons (Automated).

Cheers!
...Shu

Here are the DPR, PAS and DFM (Text) files (Copy the code and save is with proper file names as indicated):

InactMon.DPR code:
(*  InactMon.DPR CODE STARTS HERE *)
program InactMon;

uses
  Forms,
  Unit1 in 'Unit1.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.
(* InactMon.DPR CODE ENDS HERE *)


UNIT1.PAS code:
(*  UNIT1.PAS CODE STARTS HERE *)
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Timer1: TTimer;
    procedure Button1Click(Sender: TObject);
    Procedure ResetTimer;
    procedure Timer1Timer(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  MyHook: THandle;
  FHookStarted: Boolean;
  InactiviteSec: Integer;

implementation

{$R *.dfm}

function JournalProc(Code, wParam: Integer; var EventStrut: TEventMsg): Integer; stdcall;
var
  s: string;
  KbdFlag: Boolean;
begin
  {this is the JournalRecordProc}
  Result := CallNextHookEx(MyHook, Code, wParam, Longint(@EventStrut));
  {the CallNextHookEX is not really needed for journal hook since it it not
  really in a hook chain, but it's standard for a Hook}
  if Code < 0 then Exit;

  {you should cancel operation if you get HC_SYSMODALON}
  if Code = HC_SYSMODALON then Exit;
  if Code = HC_ACTION then
  begin
    {
    The lParam parameter contains a pointer to a TEventMsg
    structure containing information on
    the message removed from the system message queue.
    }

    s := '';
    KbdFlag := False;
    if EventStrut.message = WM_LBUTTONUP then
      s := 'Left Mouse UP at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);

{    if EventStrut.message = WM_LBUTTONDOWN then
      s := 'Left Mouse Down at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);}

{    if EventStrut.message = WM_RBUTTONDOWN then
      s := 'Right Mouse Down at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);}

    if (EventStrut.message = WM_RBUTTONUP) then
      s := 'Right Mouse Up at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);

    if (EventStrut.message = WM_MOUSEWHEEL) then
      s := 'Mouse Wheel at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);

    if (EventStrut.message = WM_MOUSEMOVE) then
      s := 'Mouse Position at X:' +
        IntToStr(EventStrut.paramL) + ' and Y: ' + IntToStr(EventStrut.paramH);

    if (EventStrut.message = WM_KEYUP) then
    Begin
      KbdFlag := True;
      s := 'Keyboard';
    End;

    if s <> '' then
    Begin
//On Mouse or Keyboard event, restart inactivity timer
      Form1.ResetTimer;
    End;
  end;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin

//Set a journal hook
  MyHook := SetWindowsHookEx(WH_JOURNALRECORD, @JournalProc, hInstance, 0);
  if MyHook > 0 then
  begin
    FHookStarted := True;
    ShowMessage('Journal Hook Started');
  end
  else
    ShowMessage('No Journal Hook availible');

//Start inactivity timer
  ResetTimer;

end;

procedure TForm1.ResetTimer;
begin
//This function resets the timer to start scanning for next inactivity
  Timer1.Enabled := False;
  //set inactivity time
  Timer1.Interval := InactiviteSec*1000;
  Timer1.Enabled := True;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  ShowMessage('Idle');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
//Stop shecking inactivity
  If FHookStarted then
  Begin
    Timer1.Enabled := False;
    FHookStarted := False;
    MyHook := 0;
  //Stop the hook
    UnhookWindowsHookEx(MyHook);
    ShowMessage('Journal Hook Stopped');
  End;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
//Read inactivity time from command line, default 120 sec
  If ParamStr(1) <> '' then
    InactiviteSec := StrToIntDef(ParamStr(1),120);
end;

end.

(*  UNIT1.PAS CODE ENDS HERE *)


UNIT1.DFM code:

object Form1: TForm1
  Left = 192
  Top = 107
  Width = 870
  Height = 640
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 120
    Top = 352
    Width = 75
    Height = 25
    Caption = 'Start Hook'
    TabOrder = 0
    OnClick = Button1Click
  end
  object Button2: TButton
    Left = 224
    Top = 352
    Width = 75
    Height = 25
    Caption = 'Stop Hook'
    TabOrder = 1
    OnClick = Button2Click
  end
  object Timer1: TTimer
    Enabled = False
    OnTimer = Timer1Timer
    Left = 160
    Top = 112
  end
end
>This is tricky: you want a Console App! Why? are you running in DOS? BTW, you can make a service (which would have no forms >etc.) or a formless application.

Not 100% sure if it need to be console type, I have always make console app
when I use them from login script in our Novell network, and it get small in size
and no information to user so no need for forms.

Our teachers like to walk away from their computers all the time. Need Windows XP auto lock after a certain period of time. Don’t want to use screen saver.
I look in to your cod now…
One thing I never figure out is how to past form plugin from text cod exempel.
Every time I do I need to delete and recreate.
Dark_King,
>>One thing I never figure out is how to past form plugin from text cod exempel.
:-)
I have also never tried it before!!
I think if you paste the DFM code I posted into a text file and then rename the text file as dfm, then that should do the trick: but it may cause problems.
So, if it does not work, perhaps you could do the following at design time:
1) Create a new application
2) Add two buttons on the form
3) Add a timer on the form
4) Set the timer's enabled property to false
5) Double click on the form (To get a form-click event)
6) Double click on button1 and button2 (To create on click events)
7) paste the code of unit1.

HTH,
...Shu
>>7) paste the code of unit1.
Means overwrite the contents of "Unit1.PAS" file.
I have all code in Delphi and no error from there but when I run .exe file
Is say..

Application Error.
Exception EClassNotFound in module InactMon.exe at 00013256
Class Tbutton not found
Started from scratch as you say, now it work…
Sorry Dark_King,
  I got the a similar error when I tried copying the DFM here :-(
>>Started from scratch as you say, now it work…
  Let me know if you need any clarifications.
...Shu
It work but too easy to shot down and big in file size 377k, is there a way to fix
timer without form and run as service.
It’s like this.

1: Our Network admin is newer going to use this app if it do only look workstation and is that big.
2: Student should not easy stop application.

If a service is need to be ready and installed on all workstation is ok,
And if I trigger this service from a small app its ok.
OK, now that you have a working application, let's attempt to make a service.
A few concepts:
1) A service needs to be "installed".
2) After installation (if compiled with default options) services automatically "start" every time windows starts.
3) If the service application's name is "myServce.exe", you can install it using the command
myservice.exe /install
4) Similarly A service can be uninstalled by using the command
myservice.exe /uninstall
5) A service can be manually started/stoppedfrom the Service Manager which shows a list of all installed services.
  Servce manager can be reachd as follows:
  Control Panel -> Administrative Tools -> Services
6) To create a service in Delphi (I have Delphi 7), select:
  File -> New -> Other -> (in the new tab) New Service Application
7) Step 6 creates a basic service. If you compile it, you can install/uninstall it.
8) You can place components like the timer etc. on the form-like thing.

Well, I did all the above steps and it seems my simple service without the inactivity code takes up 403 KB space. According to me, 403 KB is very small (and so is 377). Would that be OK with you? Also, let me know if you were able to create a simple service using the steps mentioned above so that we can proceed further.
...Shu
I have

Service Application
And
Service

Under new tab

install/uninstall sound greate...is it one of this...
Use Service Application from the new tab.

>>install/uninstall sound greate...is it one of this...
I didn't understand what you meant.

...Shu
Hm,, my poor English is not good.

I miss understood you “basic service” I first believed it was something
Under the new tab….*lol*


WOOW it work…

I install and uninstall basic service.
Great!
Now that you have created a service, all that remains is to add a code that starts the hook when the service starts and stop the hook when the service stops.
I hope you have figured out how to add a timer to the service.
Now, here are a few things you could do:
1) in the properties of the service, set the "interactive" property of the service to true: that will allow the service to monitor journal hooks.
2) Add a timer to the service and set its enabled property to false.
3) Paste the journalhook from the code I posted earlier in the implementation part of the service code (you may need to change "Form1.ResetTimer" to "Service1.ResetTimer" depending on the name of your service). Also paste the ontimer code. Here, you may not be able to accept paramstr, so perhaps you would have to hardcode InactiviteSec or read it from registry or an ini file.
3) You need to code onstart and onstop events of the service. Click on the events tab of object inspector for service, doubleclick in onstart and onstop to create the two event codes.
4) In onstart, Paste the code of Button1Click and in onstop, paste the code of button2click.
5) Note: The servicestart and service stop need to respond in some specific time (Specified by WiatHint property). So, it is not a very good idea to keep showmessage in the start/stop code. Also, you need to set the started and stopped variables to true in the respective codes.

I guess with this, you would have a service ready to monitor the idle time. So, once again, let me know your progress after this. Good luck!
...Shu
type
  TService1 = class(TService)
    Timer1: TTimer;
     
  private
    { Private declarations }
  public
    function GetServiceController: TServiceController; override;

    { Public declarations }
  end;


How do I decelerate?
Procedure ResetTimer;
   procedure Timer1Timer(Sender: TObject);

I get Unsatisfied forward or external declaration: 'TService1.ResetTimer'
I try this but don't work

type
  TService1 = class(TService)
    Timer1: TTimer;
   
    Procedure ResetTimer;
   procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    function GetServiceController: TServiceController; override;

    { Public declarations }
  end;
Place your cursor on
 Procedure ResetTimer;
and press shift + ctrl + C (if that is enabled in your options)
OR
ADD
procedure TService1.ResetTimer;
begin
//the code
end;

in the implementation part.
procedure TService1.Timer1Timer(Sender: TObject);

i forget to chang this....
Your code should look something like:


unit SrvcUnit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs,
  ExtCtrls;

type
  TService1 = class(TService)
    Timer1: TTimer;
    Procedure ResetTimer;
    procedure ServiceStart(Sender: TService; var Started: Boolean);
    procedure ServiceStop(Sender: TService; var Stopped: Boolean);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private declarations }
  public
    function GetServiceController: TServiceController; override;
    { Public declarations }
  end;

var
  Service1: TService1;
  MyHook: THandle;
  FHookStarted: Boolean;
 

implementation

{$R *.DFM}

function JournalProc(Code, wParam: Integer; var EventStrut: TEventMsg): Integer; stdcall;
var
  s: string;
  KbdFlag: Boolean;
begin
  {this is the JournalRecordProc}
  Result := CallNextHookEx(MyHook, Code, wParam, Longint(@EventStrut));
  {the CallNextHookEX is not really needed for journal hook since it it not
  really in a hook chain, but it's standard for a Hook}
  if Code < 0 then Exit;

  {you should cancel operation if you get HC_SYSMODALON}
  if Code = HC_SYSMODALON then Exit;
  if Code = HC_ACTION then
  begin
    {
    The lParam parameter contains a pointer to a TEventMsg
    structure containing information on
    the message removed from the system message queue.
    }

    s := '';
    KbdFlag := False;
    if EventStrut.message = WM_LBUTTONUP then
      s := 'Left Mouse UP at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);

{    if EventStrut.message = WM_LBUTTONDOWN then
      s := 'Left Mouse Down at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);}

{    if EventStrut.message = WM_RBUTTONDOWN then
      s := 'Right Mouse Down at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);}

    if (EventStrut.message = WM_RBUTTONUP) then
      s := 'Right Mouse Up at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);

    if (EventStrut.message = WM_MOUSEWHEEL) then
      s := 'Mouse Wheel at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);

    if (EventStrut.message = WM_MOUSEMOVE) then
      s := 'Mouse Position at X:' +
        IntToStr(EventStrut.paramL) + ' and Y: ' + IntToStr(EventStrut.paramH);

    if (EventStrut.message = WM_KEYUP) then
    Begin
      KbdFlag := True;
      s := 'Keyboard';
    End;

    if s <> '' then
    Begin
//On Mouse or Keyboard event, restart inactivity timer
      Service1.ResetTimer;
    End;
  end;
end;

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  Service1.Controller(CtrlCode);
end;

function TService1.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;

procedure TService1.ResetTimer;
begin
  //This function resets the timer to start scanning for next inactivity
  Timer1.Enabled := False;
  //set inactivity time
  Timer1.Interval := 120*1000;
  Timer1.Enabled := True;
end;

procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
//Set a journal hook
  MyHook := SetWindowsHookEx(WH_JOURNALRECORD, @JournalProc, hInstance, 0);
  if MyHook > 0 then
  begin
    FHookStarted := True;
    ShowMessage('Journal Hook Started');
  end
  else
    ShowMessage('No Journal Hook availible');

//Start inactivity timer
  ResetTimer;
end;

procedure TService1.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
//Stop shecking inactivity
  If FHookStarted then
  Begin
    Timer1.Enabled := False;
    FHookStarted := False;
    MyHook := 0;
  //Stop the hook
    UnhookWindowsHookEx(MyHook);
    ShowMessage('Journal Hook Stopped');
  End;
end;

procedure TService1.Timer1Timer(Sender: TObject);
begin
  ShowMessage('Idle');
end;

end.
Something most be wrong..

Not 100% shore how to start.

In old APP you use

procedure TForm1.FormCreate(Sender: TObject);
begin
//Read inactivity time from command line, default 120 sec
  If ParamStr(1) <> '' then
    InactiviteSec := StrToIntDef(ParamStr(1),120);
end;

I try

procedure TService1.ServiceCreate(Sender: TObject);
begin
InactiviteSec := 10;
end;


for 10 sec but nothing happen.




*****code******

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs,
  ExtCtrls, ahmsystemclass, ahmtappstarter;

type
  TService1 = class(TService)
    AHMAppStarter1: TAHMAppStarter;
    Timer1: TTimer;
       Procedure ResetTimer;
     procedure Timer1Timer(Sender: TObject);
    procedure ServiceStart(Sender: TService; var Started: Boolean);
    procedure ServiceStop(Sender: TService; var Stopped: Boolean);
    procedure ServiceCreate(Sender: TObject);

  private
    { Private declarations }
  public
    function GetServiceController: TServiceController; override;

    { Public declarations }
  end;

var
  Service1: TService1;
   MyHook: THandle;
  FHookStarted: Boolean;
  InactiviteSec: Integer;
implementation

{$R *.DFM}

procedure ServiceController(CtrlCode: DWord); stdcall;
begin
  Service1.Controller(CtrlCode);
end;

function TService1.GetServiceController: TServiceController;
begin
  Result := ServiceController;
end;
 
function JournalProc(Code, wParam: Integer; var EventStrut: TEventMsg): Integer; stdcall;
var
  s: string;
  KbdFlag: Boolean;
begin
  {this is the JournalRecordProc}
  Result := CallNextHookEx(MyHook, Code, wParam, Longint(@EventStrut));
  {the CallNextHookEX is not really needed for journal hook since it it not
  really in a hook chain, but it's standard for a Hook}
  if Code < 0 then Exit;

  {you should cancel operation if you get HC_SYSMODALON}
  if Code = HC_SYSMODALON then Exit;
  if Code = HC_ACTION then
  begin
    {
    The lParam parameter contains a pointer to a TEventMsg
    structure containing information on
    the message removed from the system message queue.
    }

    s := '';
    KbdFlag := False;
    if EventStrut.message = WM_LBUTTONUP then
      s := 'Left Mouse UP at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);

{    if EventStrut.message = WM_LBUTTONDOWN then
      s := 'Left Mouse Down at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);}

{    if EventStrut.message = WM_RBUTTONDOWN then
      s := 'Right Mouse Down at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);}

    if (EventStrut.message = WM_RBUTTONUP) then
      s := 'Right Mouse Up at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);

    if (EventStrut.message = WM_MOUSEWHEEL) then
      s := 'Mouse Wheel at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);

    if (EventStrut.message = WM_MOUSEMOVE) then
      s := 'Mouse Position at X:' +
        IntToStr(EventStrut.paramL) + ' and Y: ' + IntToStr(EventStrut.paramH);

    if (EventStrut.message = WM_KEYUP) then
    Begin
      KbdFlag := True;
      s := 'Keyboard';
    End;

    if s <> '' then
    Begin
//On Mouse or Keyboard event, restart inactivity timer
      Service1.ResetTimer;
    End;
  end;
end;
 procedure TService1.Timer1Timer(Sender: TObject);
begin
  ShowMessage('Idle');
end;

  procedure TService1.ResetTimer;
begin
//This function resets the timer to start scanning for next inactivity
  Timer1.Enabled := False;
  //set inactivity time
  Timer1.Interval := InactiviteSec*1000;
  Timer1.Enabled := True;
end;

procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
 //Set a journal hook
  MyHook := SetWindowsHookEx(WH_JOURNALRECORD, @JournalProc, hInstance, 0);
  if MyHook > 0 then
  begin
    FHookStarted := True;
    ShowMessage('Journal Hook Started');
  end
  else
    ShowMessage('No Journal Hook availible');

//Start inactivity timer
  ResetTimer;
end;

procedure TService1.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
 //Stop shecking inactivity
  If FHookStarted then
  Begin
    Timer1.Enabled := False;
    FHookStarted := False;
    MyHook := 0;
  //Stop the hook
    UnhookWindowsHookEx(MyHook);
    ShowMessage('Journal Hook Stopped');
  End;
end;

procedure TService1.ServiceCreate(Sender: TObject);
begin
InactiviteSec := 10;
end;

end.
Here's a checklist:
0) Does the code compile mow? :-)
1) Have you set the interactive property if the service to true?
2) Heve you created procedures like service start/ service stop and timer1timer by double-clicking in the events tab?

...Shu
>>0) Does the code compile mow? :-)
sorry, read it as
0) Does the code compile now? :-)

Also, move the InactiviteSec := 10; to the service start event.

...Shu
Also, add started/stopped  := true in the codes. Like:

procedure TService1.ServiceStart(Sender: TService; var Started: Boolean);
begin
  Started := False;//ADDED
 //Set a journal hook
  MyHook := SetWindowsHookEx(WH_JOURNALRECORD, @JournalProc, hInstance, 0);
  if MyHook > 0 then
  begin
    Started := True;//ADDED
    FHookStarted := True;
    ShowMessage('Journal Hook Started');//remove this after testing
  end
  else
    ShowMessage('No Journal Hook availible');

//Start inactivity timer
  ResetTimer;
end;

procedure TService1.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
  Stopped := True;//ADDED
 //Stop shecking inactivity
  If FHookStarted then
  Begin
    Timer1.Enabled := False;
    FHookStarted := False;
    MyHook := 0;
  //Stop the hook
    UnhookWindowsHookEx(MyHook);
    ShowMessage('Journal Hook Stopped');//remove this after testing
  End;
end;
I don’t now how to now it,s working if I install as service
I need to start the service from XP it is set as Automatic but I try run..  

No error in Delphi but not shore on watt is in code that’s start timer..

http://www3.park.se/~jan/delphi6/ 
And when the service install, Windows show messages, is there a flag fore silent install?
Dark_King,
  All seems perfect except that the timer's ontimer event is not linked. At design time, open Unit1 in delphi, press F12, then click on the timer component, press F11. In the timer's object inspector, click on the events tab. You will see an ontimer event there. Drop down its combo box and select Timer1Timer.
  Then compile and install the service. It should work.
  Let me know the progress.
...Shu
ontimer event is not linked > fixed..


I can install as Service but need to start it cold it be started when I use /install
Then it show mess “Journal Hook Started” but if I is inactive in 4 minutes
nothing happen…
ASKER CERTIFIED SOLUTION
Avatar of snehanshu
snehanshu

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
The only thing I want it to show  is this “Idle”

procedure TInactMon.Timer1Timer(Sender: TObject);
begin
ShowMessage('Idle');

end;

is this procedure activate on timer count to end?

I can’t figure out if it tracing inactivity or not, only activity from service
is when I use

ShowMessage('Journal Hook Started')

Nothing else is showing and I have remove comment marks from all ShowMessage

I give you the point NOW and if you figure watts wrong you can mess here
ore send me a mail

jan@park.se
Dark_King,
  I am emailing you the code that shows Idle message on my system after 15 seconds. If it works, do post it at the link where you posted the zip file earlier so that others can see: its against EE guidelines to use emails, but if you post the code I mail you at the link, then I guess we should be fine.
  A few things:
  You would need to manually start the service after installing: it autostarts only on restart, not on install.
  Idle means no keyboard or mouse movements. So, after starting the service, don't touch your PC for around 20 seconds and then you should be able to see the idle message.
  And, I don't mind not getting extra points, but I do mind a B grade unless I am given a chance to explain and I can't. You could have waited till we solved this and then awarded a grade.
  Any way, let me know how the code runs on your system.
Cheers!
...Shu
Sorry for that I starting to feel I getting on your nerves,
And I feel stupid to ask question for every post you made.

It while never happen again,

And is there I way to fix the B grade.

Code I mail you at the link > Fixed

Last Question:

You say “You would need to manually start the service after installing:”
If it’s true I can’t use this Service, Need to star from login script..
>>Need to star from login script
Refer this thread: create an application that manually starts your service and run this app from the login script.
https://www.experts-exchange.com/questions/20154281/Manually-Starting-Automatic-Services.html

>>And is there I way to fix the B grade.
You could post a question in CS to change the grade.

...Shu

And,
  From your mail:
>>Watt is I doing wrong.
>>
>>I have tested the .exe file in your Project1.zip and not working.
>>I have complied .exe from my Delphi and not working.
>>
>>
>>I test like this.
>>
>>From CMD I wrote “Project1.exe /install”
>>XP says service was installed.
>>
>>And then from Windows XP Service I right click on InactMon
>>and start “RUN”. Wait for 1 minutes and nothing happen.

Checklist:
1) Well, does the application we created initially work?
2) Uninstall Project1.exe
3) Try deleting ALL versions of Project1.exe
3) Then compile the service (after deleting all exes of this service)
4) Then install this service and run (start) it.
5) Take screen captures of the general and logon tabs from the properties of the service and post it at the site (If it still doesn't work).

Good luck,
...Shu
Dark_King,
  When I install and start it, it does not work for me too.
  But after stopping and then starting, it does work.
  Can you check if the same thing happens at your end?
...Shu
No, not working
I while test de service on another computer.
Dark_King,
  Here's a solution:
  Instead of a service, you could switch back to an application which does not show the main form.
  You could set its application.title to null to hide it from task manager application's list.
  You could set it's main form's borderstyle to bsToolWindow to hide it from the alt-tab list.
  Then you could use formcreate to start the hook.
  Here's the code of DPR and PAS files. The form is the same as the one we used in the discussions above. Only, it's visible property is set to false and borderstyle changed to bsToolWindow.

Code:
(* Code For IdleProject1.DPR: *)

program IdleProject1;

uses
  Forms,
  IdleUnit in 'IdleUnit.pas' {Form1};

{$R *.res}

begin
  Application.ShowMainForm := False;
  Application.Title := '';
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;

end.

(*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*)

(* Code for IdleUnit.PAS *)


(* This application can be used to monitor the idle time of a computer *)
(*
 The form's border style is set to bsToolWindow to hide it
 from the Alt-Tab list.
 The Application.Title is set to null ('') to hide it from the Application list.
*)


unit IdleUnit;

interface

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

type
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Timer1: TTimer;
    procedure Button1Click(Sender: TObject);
    Procedure ResetTimer;
    procedure Timer1Timer(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  MyHook: THandle;
  FHookStarted: Boolean;
  InactiviteSec: Integer;

implementation

{$R *.dfm}

function JournalProc(Code, wParam: Integer; var EventStrut: TEventMsg): Integer; stdcall;
var
  s: string;
  KbdFlag: Boolean;
begin
  {this is the JournalRecordProc}
  Result := CallNextHookEx(MyHook, Code, wParam, Longint(@EventStrut));
  {the CallNextHookEX is not really needed for journal hook since it it not
  really in a hook chain, but it's standard for a Hook}
  if Code < 0 then Exit;

  {you should cancel operation if you get HC_SYSMODALON}
  if Code = HC_SYSMODALON then Exit;
  if Code = HC_ACTION then
  begin
    {
    The lParam parameter contains a pointer to a TEventMsg
    structure containing information on
    the message removed from the system message queue.
    }

    s := '';
    KbdFlag := False;
    if EventStrut.message = WM_LBUTTONUP then
      s := 'Left Mouse UP at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);

{    if EventStrut.message = WM_LBUTTONDOWN then
      s := 'Left Mouse Down at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);}

{    if EventStrut.message = WM_RBUTTONDOWN then
      s := 'Right Mouse Down at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);}

    if (EventStrut.message = WM_RBUTTONUP) then
      s := 'Right Mouse Up at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);

    if (EventStrut.message = WM_MOUSEWHEEL) then
      s := 'Mouse Wheel at X pos ' +
        IntToStr(EventStrut.paramL) + ' and Y pos ' + IntToStr(EventStrut.paramH);

    if (EventStrut.message = WM_MOUSEMOVE) then
      s := 'Mouse Position at X:' +
        IntToStr(EventStrut.paramL) + ' and Y: ' + IntToStr(EventStrut.paramH);

    if (EventStrut.message = WM_KEYUP) then
    Begin
      KbdFlag := True;
      s := 'Keyboard';
    End;

    if s <> '' then
    Begin
//On Mouse or Keyboard event, restart inactivity timer
      Form1.ResetTimer;
    End;
  end;
end;


procedure TForm1.Button1Click(Sender: TObject);
begin

//Set a journal hook
  MyHook := SetWindowsHookEx(WH_JOURNALRECORD, @JournalProc, hInstance, 0);
  if MyHook > 0 then
  begin
    FHookStarted := True;
//    ShowMessage('Journal Hook Started');
  end;
//  else
//    ShowMessage('No Journal Hook availible');

//Start inactivity timer
  ResetTimer;

end;

procedure TForm1.ResetTimer;
begin
//This function resets the timer to start scanning for next inactivity
  Timer1.Enabled := False;
  //set inactivity time
  Timer1.Interval := InactiviteSec*1000;
  Timer1.Enabled := True;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  ShowMessage('Idle');
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
//Stop shecking inactivity
  If FHookStarted then
  Begin
    Timer1.Enabled := False;
    FHookStarted := False;
    MyHook := 0;
  //Stop the hook
    UnhookWindowsHookEx(MyHook);
//    ShowMessage('Journal Hook Stopped');
  End;

end;

procedure TForm1.FormCreate(Sender: TObject);
begin
//Read inactivity time from command line, default 120 sec

  Application.Title := '';//this will hide the program from the task manager
//  If ParamStr(1) <> '' then
  InactiviteSec := StrToIntDef(ParamStr(1),15);
  Button1Click(Self);
  Form1.Visible := False;

end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Button2Click(Self);
end;

end.
OK

This is working and I have updated all files in http://www3.park.se/~jan/delphi6/

I try to make my app ready and look in to the service later.
Great!
...Shu