Link to home
Start Free TrialLog in
Avatar of Henrie
HenrieFlag for Belgium

asked on

how to determine the system is idle?

Our scenario:
We want use alle workstations in our company to do work on a big project that involves converting and generating about 90,000+ files a couple of times a month.
It is a processing intensive application, so we want to use all workstations when they're not in use.
We are developing an application that's running in the background for starting conversion and generation of files.

The question:
Using Delphi 5, how do you determine if the system is idle?
We want to start the jobs from a queue when the system where the application is running is idle.

I'm relaying this question for a friend, so examples are also very welcome, thanks in advance,
H.
Avatar of inthe
inthe

hi,
there is the Application.OnIdle event which ,may be enough or
you could try setting the priority of your app to IDLE

ie:
var
  Pid : DWORD;
  Tid,Proc : THandle;
 begin
  Pid := GetCurrentProcessID;
  Proc := OpenProcess(PROCESS_SET_INFORMATION,false,Pid);
  SetPriorityClass(Proc, IDLE_PRIORITY_CLASS );
  Tid := GetCurrentThread;
  SetThreadPriority(Tid, THREAD_PRIORITY_IDLE);
 end;

{not tested the above at idle so dont know how well if at all it works}

or you maybe could do something with the screensaver ..
you can put your job on Application's OnIdle event.
but it means that the application is idle, not
the system.
you can read delphi help about TApplication.OnIdle.
Depends on what you define as idle. Do you mean no user input? Low amount of processing?

If if it was just a case of no user input then you could simply check the mouse and keyboard state every second - If no change for several seconds then the system is idle

The Neil =:)
Avatar of Henrie

ASKER

Thanks to all for your quick response!

We have to check the complete system to be idle, so no mouse or keyboard input and no other application running (or taking processing time).

Inthe: I think screensaver isn't an option because IMHO it doesn't check for running programs or hdd activity. The program should check on low processing and no user input.
The idea of setting the priority of the application to idle in code? We'll try it. The question then is: if the application starts running if the system is idle, then the system isn't idle anymore, so on its turn the application itself doesn't get the processing time it needs...?

I'll be back, thanks again,
H.
Avatar of Henrie

ASKER

Inthe, your solution didn't work :(
The application starts as soon as the system is idle for only just a milisecond... It is great for sharing processortime between apps, but not for us.

upped the points to 150, because I realize this isn't so easy...

any ideas?

You could always write a screen saver app that does the heavy processing.  Like the SETI screensaver.

You could also write a small app that's always running on the machine.  This app checks the CPU activity, and also looks for any Screensaver start messages.

When the program gets a screensaver about to start message
WM_SYSCOMMAND with the WParam set to SC_SCREENSAVE
Set the return value to 0 and the screen saver won't start or don't alter the message and the screensaver will start.


You can check the amount of CPU activity over the last say 5 minutes.

If the activity has been constantly low - i.e. Less that 15% Max allow the screensaver to start.

Otherwise, don't let the screensaver start.

When the user want to use the machine they will simply shutdown the screensaver, and the little app won't let the screensaver start for another 5 minutes (the screensavers processing would be detected).


Hope this give's you some ideas.

Install a systemwide mouse & keyboard hook to determine if the system is idle.

Have a look at the TWatch component.

http://www.bome.com/delphi/watch/Watch110.zip
(42KB)
This component enables you to track some system events. Currently implemented are Shell events (i.e. Activation, Creation, Closure of Applications), and Keyboard events (when something is typed in another application) and Mouse events (everything what the mouse does).
Avatar of Henrie

ASKER

The little app that's running in the background has to start the big app that starts processing/executing the jobs in queue. We don't want to use the screensaver app possibility. Users can disable/change or forget it.
Colin, a question: How do I check for cpu activity in Delphi? Have you got a link or an example?
We'll combine it with the TWatch component mentioned in the comment from Tasomia and that should do it!
The cpu component is necesary because keyboard en mouse activity isn't enough to check. i.e. The copying of files has also to be determined.
Thanks for all your effort!
H.

Honestly, I've never tried to get the CPU useage Statistic.
So I don't know, I'll have a look at work today and see if there's any examples. I'm sure that there will be somewhere.
ASKER CERTIFIED SOLUTION
Avatar of bpouydog
bpouydog

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
Here's a coworker's solution:

You have to use TApplication.OnIdle event. Write an OnIdle event
handler to perform special processing when an application is
idle. An application is idle when it is not processing code. For
example, an application is idle when it is waiting for input
from the user. The TIdleEvent type is the type of the OnIdle
event. TIdleEvent has a Boolean parameter Done that is True by
default. When Done is True, the Windows API WaitMessage function
is called after OnIdle returns. WaitMessage yields control to
other applications until a new message appears in the message
queue of the application. When Done is False, the application
does not yield control to other applications while it is not
busy. OnIdle is called only once, as the application transitions
into an idle state. It is not called continuously unless Done is
set to False. Applications that set Done to False consume an
inordinate amount of CPU time, which affects overall system performance.
Attached: example of using OnIdle event to realize a background processing.    

##############################################################################################################

The following code demonstrates background processing using the OnIdle event,
and using the HandleMessage method to permit messages or background processing
to get through.

Note: You must add MyIdleHandler to the Form1 methods.

var
{ global variables to show the order of events. }
  XPos, YPos, Delta: integer;

{ This is a utility procedure to display messages }
{ add this at the beginning of the implementation section }
procedure StatusMsg(MyForm : TForm; Canvas : TCanvas; Message : string; Bkg : Boolean);
begin
  if not bkg then
    Canvas.Font.Style := [fsBold]; {foreground messages are bold }
  Canvas.TextOut(XPos, YPos, Message);
  if not bkg then

    Canvas.Font.Style := [];
  { change Xpos and YPos to prepare for the next message }
  Ypos := Ypos + Delta;
  if YPos >= MyForm.ClientHeight then
  begin
    YPos := 10;
    Xpos := Xpos + 100;
  end;
  if (Xpos >= MyForm.ClientWidth - 100) then
  begin
    if (Canvas.Font.Color = clRed) then
      Canvas.Font.Color := clBlack;
    else
      Canvas.Font.Color := clRed;
    Xpos := 10;

  end;
end;


{This is the Form's OnCreate event handler.  }

{ It initializes global variables and sets the  the OnIdle event handler }

procedure TForm1.FormCreate(Sender: TObject);

begin
  Button1.Caption := 'Do not yield';
  Button2.Caption := 'Handle Message';
  Application.OnIdle:= MyIdleHandler;
  XPos := 10;
  YPos := 10;
  Delta := Abs(Canvas.Font.Height) + 1;
 end;

{ This is the OnIdle event handler. It is set in the Form's OnCreate event
handler, so you need only add it as a private method of the form.  
Usually it would perform some background processing for the application.  
This one simply writes a message to let you know when it's there. }

procedure TForm1.MyIdleHandler(Sender: TObject; var Done: Boolean);
begin
  StatusMsg(TForm1, Canvas, 'This represents a background process.', True);
end;

  { Set this method as the OnClick event handler of Button1.  It performs a calculation without yielding to other messages or idle time. }
procedure TForm1.Button1Click(Sender: TObject);
var
  I, J, X, Y: Word;
begin
  StatusMsg(TForm1, Canvas, 'The Button1Click handler is starting', False);

  I := 0;
  J := 0;
  while I < 10 do
  begin
    Randomize;
    while J < 10 do
    begin
      Y := Random(J);
      Inc(J);
    end;
    X := Random(I);
    Inc(I);
  end;
  StatusMsg(TForm1, Canvas, 'The Button1Click handler is done', False);
end;

{ Set this method as the OnClick event handler of Button2.  
It performs a calculation but calls HandleMessage to allow idle time or
asynchronous message processing. }

procedure TForm1.Button2Click(Sender: TObject);
var
  I, J, X, Y: Word;
begin
 StatusMsg(TForm1, Canvas, 'The Button2Click handler is starting', False);
  I := 0;
  J := 0;
  while I < 10 do
  begin
    Randomize;
    while J < 10 do
    begin
      Y := Random(J);
      Inc(J);
      Application.HandleMessage; { yield to OnIdle or other messages }
    end;
    X := Random(I);

    Inc(I);
  end;
  StatusMsg(TForm1, Canvas, 'The Button2Click handler is done', False);
end;

 
     
  Below is the sample codes to determine if the system is idle in
Delphi:

IDLEFORM.PAS
unit Idleform;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages,
  Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls, StdCtrls, Gauges;

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    procedure FormCreate(Sender: TObject);
    procedure AppIdle(Sender: TObject;
                      var Done: Boolean);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
  Application.OnIdle := AppIdle;
end;

procedure TForm1.AppIdle(Sender: TObject;
                         var Done: Boolean);
begin
  Panel1.Caption :=
    IntToStr(GetFreeSpace(Word(0))) +
      ' Bytes Free (Global)';
end;

end.

For more examples, please visit the following websites:

http://www.elementkjournals.com/ddj/back/ddj1_3d.htm

http://www.borland.com/techpubs/delphi/delphi5/dg/threads.html

To determine if the system is idle, below website provides the
full source codes:

http://www.planet-source-code.com/xq/ASP/txtCodeId.823/lngWId.-
1/qx/vb/scripts/ShowCode.htm

 
Avatar of Henrie

ASKER

After reformatting the code, this is the right one. Thanks bpouydog!
Of course many thanks to all others too!

H.
I'm trying to do this exact same thing and the accepted answer above isn't working for me.. It may very well be due to the fact that I have the code in wrong..

Can someone send me the actualy .pas file for the accepted answer above please..

palamedes@rocketmail.com

Thanks.
Never mind.. Got it to work..  Thanks..