Link to home
Start Free TrialLog in
Avatar of aj85
aj85

asked on

TIMER


My application makes a call to execute another application.  The problem that I have is that I need to execute that call every 30 minutes, i.e. 2:00, 2:30, 3:00.  How can I use the Timer component to do this, or is there a better way to implement this?  Please give example.

Thanks
TJ  
Avatar of inthe
inthe

hi,
as long as the app your starting is closing itself then it should be fine.
the ttimer interval will be 30*60*1000 milliseconds.
so you could set timer to 1800000
was there anything else you wondered about this?
like did you want to do the event at exactly 2:00 then 2:30 etc or is it ok to do at 2:13 then 2:43 etc..

 Regards Barry
Avatar of aj85

ASKER



Inthe/Barry,


You are very close and your question is a good one.  Actually, what I need to do is have the event occur at 2:00, 2:30 etc., exactly. So, are you saying that all I need to do is set the Timer to fire by 1800000 intervals?  The other app does closes itself.  If that is what you are saying then I will try this to see if it works.  Also before I award you the points could you please supply sample code as I requested?  I will then gladly give you the well earned points.

Thanks,
TJ

P.S.  I was just wondering, what would I need to do if I wanted to set the intervals at 2:13 then 2:43 etc., as you stated in your question?
ok example coming in a while.
hello
first the simple example that does an event every 30 mins :

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
Timer1.interval := 5000; //set the interval to 1800000 for 30 mins
timer1.enabled := true; //start the timer
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
//shellexecute uses shellapi so add it to uses section
shellexecute(handle,'open',pchar('notepad.exe'),nil,nil,sw_normal);
Application.processmessages; //dont lock up the pc
end;

end.






and this is the second example  for looking for a certain time then starting the events.
it uses 2 timers instead of one.
the first timer is main timer with executing event and the second timer is the timer that sits in a loop and checks until it is the time we specify.
when the specified time is here is starts the first timer and stops itself.
hope you follow it ok i tried to comment it as best i could.
Regards Barry



unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    Timer2: TTimer;
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Timer2Timer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
//Timer1.Interval := 1800000;
// the above line is for 30 minute interval
//timer1 interval at 5000 below is for testing only
Timer1.interval := 5000; //set the intervals
Timer2.interval := 1000;
timer1.enabled := false; //disable until we get correct time
timer2.enabled := true;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
//shellexecute uses shellapi so add it to uses section
//shellexecute(handle,'open',pchar('notepad.exe'),nil,nil,sw_normal);
showmessage('timer event');
Application.processmessages; //dont lock up the pc
end;

procedure TForm1.Timer2Timer(Sender: TObject);
var
  IsItTime: TDateTime;
  Hour, Min, Sec, MSec: Word;
 begin
  IsItTime := Now;
  DecodeTime(IsItTime, Hour, Min, Sec, MSec);
  if (Hour = 1) and (Min = 51)
  // this will loop until time is 1:51am then
  // it will stop the loop and start timer1
  then
  begin
   showmessage('yep'); //ok got the time
   timer2.enabled:=false; //turn off this timer we dont need it anymore
   timer1.enabled := true;//start the main timer
  end;
end;

end.
I think NetScheduleJobAdd may be another solution.

The NetScheduleJobAdd

function submits a job to run at a specified future time and date. This function requires that the Schedule service be started at the computer to which the job is submitted.

Security Requirements

Only members of the Administrators local group can successfully execute NetScheduleJobAdd on a remote server.

NET_API_STATUS NetScheduleJobAdd(

    LPWSTR Servername,      
    LPBYTE Buffer,      
    LPDWORD JobId      
   );      
 

Parameters

Servername

Pointer to a Unicode string containing the name of the remote server on which the function is to execute. A NULL pointer or string specifies the local computer.

Buffer

Pointer to a buffer containing an AT_INFO structure describing the job to be submitted.

JobId

Pointer to a job identifier for a newly submitted job. This entry is valid only if the function returns successfully.

 

If you set DaysOfMonth and DaysOfWeek to zero, then the job executes only once, the first time JobTime at the server is reached. After being executed, the job will be deleted.
If one sets bits in DaysOfMonth and/or DaysOfWeek, but do not set the bit flag JOB_RUN_PERIODICALLY, a job will execute at JobTime once for each day listed in days bitmasks. See the AT_INFO structure for a descritpion of the bitmasks. After each execution, the corresponding bit in days bitmasks will be cleared. Once the last bit in days bitmasks is cleared, the job will be deleted.

If one sets bits in DaysOfMonth and/or DaysOfWeek, and at the same time set the bit flag JOB_RUN_PERIODICALLY, a job executes at JobTime whenever a day with the corresponding bit in days bitmasks is reached. See the AT_INFO structure for a descritpion of the bitmasks. This job is thus executed periodically and does not get deleted as a result of repeated executions. The only way to delete this job is by an explicit call to NetScheduleJobDel.

good luck

hubdog
Hi aj85,

I think the simplest solution is:

1.Create external text file containing exactly the moments for starting application in string format like this:

10:23
12:13
14:01
....
etc

This file will be shedule itself. You may develop it adding name of application to be run at given time.

2.Set Timer's interval to 1 minute (60*1000 mS).

3.Write Timer's event procedure which gets current time (function Now), and
compares it with shedule items (naturally - converted into TDateTime).
If current time is equal to some of shedule items, then run external app.

About timer firing: the timer is fired not 60*1000 times but once in 60*1000 mS.

If there are some problems with converting time to string and back, let me know.

Hope helps.

Regards,
Jo.
Ofcourse, you could create shedule file as "file of TDateTime", but it will be a bit difficult to maintain it :-).

Jo.
aj85,
have you tried the examples i paste here yet?
Barry,
It is not clear for me what first timer in your second example does?
It seems to me the first timer begins to start external app every second after time for second timer has come up. Isn't enough one timer to check if the time is up and start application?

Jo.
Hello again,

This is an example of what I'm speaking for in my comments:
-------------------------------

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    Timer1: TTimer;
    procedure FormCreate(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    function  HourInShedule(Moment:TDateTime):Boolean;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}


procedure TForm1.FormCreate(Sender: TObject);
begin
  ListBox1.Items.LoadFromFile('SHEDULE.TXT');
  Timer1.Interval:=60000; //wakeup timer every minute
  Timer1.Enabled:=True;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
   if HourInShedule(Time)
     then WinExec (...);   //HERE: execute external application using WinExec or CreateProcess
end;

function  TForm1.HourInShedule(Moment:TDateTime):Boolean;
var I:Integer;
    ST,SM:String;
begin
  SM:=TimeToStr(Moment); //string format:  hh:mm:ss
  SM:=Copy(SM,1,5);      //extract hh:mm from string
  Result:=False;
  for I:=0 to ListBox1.Items.Count-1 do
    begin
      ST:=Copy(ListBox1.Items[I],1,5);
      Result:=Result OR (ST=SM);
    end;
end;

end.
-------------------------------

SHEDULE.TXT contents things like this
(no empty rows, no leading spaces!!!):

10:23
12:13
14:01


NOTE:if you want to start app at exact moments given by hh:mm:ss you should
set timer interval to 1000 (1 second), add seconds field to shedule items and
make needed changes in TForm1.HourInShedule function:
SM:=(Copy(SM,1,8));
....
ST:=Copy(ListBox1.Items[I],1,8);
....

in this case SHEDULE.TXT will look this way:

10:23:01
12:13:45
14:01:59


Regards,
Jo.
Maybe not much new, but the problem seems simple, I use some info from other comments:

You could use the info from inthe (rejected anser), but if you want to start something at 1.00, 1.30, et cetera. You should add:

First set the timer at an interval of 1 minute (or some seconds if you want 1.00.00 and not possibily 1.00.59)

global var:
ExactHalfOurReached : boolean;

ExactHalfOurReached := false;


In the OnTimer Event you:
var
  DateTime : TDateTime;
  str : string;
begin
if not ExactHalfOurReached then
  begin
  DateTime := Time;  
  str := TimeToStr(DateTime);
  //now find a way to put the exact   minute from the str in an integer;
  //if the integer = 0 or 30, you:
reset the timer interval to once every 30 minuts and make ExactHalfOurReached true.
  end
else //ExactHalfOurReached true
  startYourApp;
end;


Greetings,
Floris.
Avatar of aj85

ASKER


OK,


I have tried all the examples that I have been given and I am not seeing the result I expected.  Below is the procedure that I  execute the other application from using ShellExec.  What I need is for that application to be called everyday at a constent time i.e. 3:15.  


Code:


procedure Download ( progressGauge:TGauge; statusLabel: TLabel);
var
       myUpDown: TUpDown;
        MergeCmd: String;
       
 begin
   myUpDown := TUpDown.Create( progressGauge, statusLabel );

   try
         myUpDown.BuildDownload;
      except
         on E: Exception do
            begin
               ShowMessage( 'Error in processing download. ' + #10#13 + E.message );
         end;
   end;
      MergeCmd := 'C:\MSSQL7\SCRIPTS\MERGE.EXE';  {Path to the Merge application //TJ}
      myUpDown.Free;
begin
     then
   ShellExecute(Application.Handle,'Open',PChar(MergeCmd), nil, nil, 5 );  {Call the Merge application and Execute it! //TJ}
   ShowMessage('Merge Complete!');  {Confirm completion //TJ}
   end;
  myUpDown.Free;
   end;


I hope this gives a clear picture of what it is that I am trying to do.  If I can get an answer from this I will award the points.  Please try to answer ASAP.

Thanks again for everyone's help with this.


TJ


ASKER CERTIFIED SOLUTION
Avatar of inthe
inthe

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
all those showmessages are for testing to show it works ;-)
here is a c++ builder demo written by others,it has been tested.

LPDWORD jobid=new DWORD;
 NET_API_STATUS err;
 AT_INFO at_info;
  at_info.JobTime=3*60*60*1000+15*60*1000;//miliseconds from midnight to 3:15
  at_info.DaysOfMonth=4294967295;
  at_info.DaysOfWeek=255;
  at_info.Command="C:\WINNT\system32\sol.exe";
  at_info.Flags=JOB_RUN_PERIODICALLY ;
  if(err=NetScheduleJobAdd(NULL,(LPBYTE )&at_info
      ,jobid))ShowMessage("ok");
  else ShowMessage("error");

#inlude <lmat.h>
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <windows.h>
#include <lmcons.h>
#include <lmaccess.h>
#include <lmerr.h>
#include <lmapibuf.h>
#include <lmuse.h>

//here is a delphi demo.
unit insjob;

interface

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

type
  TAT_INFO = record
    JobTime: DWord;
    DaysOfMonth: DWord;
    DaysOfWeek: UCHAR;
    Flags: UCHAR;
    Command: PWideChar;
  end;

  PAT_INFO = ^TAT_INFO;
  NET_API_STATUS = LongInt;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation
{$R *.DFM}
function NetScheduleJobAdd(ServerName: PWideChar; Buffer: PAT_INFO; var JobID: PInteger): NET_API_STATUS; external 'netapi32.dll' name 'NetScheduleJobAdd';

procedure TForm1.Button1Click(Sender: TObject);
var
  ATInfo:PAT_Info;
  jobid:Pinteger;
begin
  getmem(atinfo,sizeof(TAt_info));
  getmem(jobid,sizeof(integer);
  atinfo^.jobtime:=3*60*60*1000+15*60*1000;//miliseconds from midnight to 3:15
  atinfo^.DaysOfMonth:=4294967295;
  atinfo^.DaysOfWeek:=255;
  atinfo^.command:=C:\WINNT\system32\sol.exe';
  atinfo^.flags:=JOB_RUN_PERIODICALLY;
  if NetScheduleJobAdd(nil,atinfo,jobid)=noerror then
    showmessage('ok');
  freemem(jobid);
  freemem(atinfo);
end;
//it has not been tested because i have not c++ h file (and its h file is not translate into pas by delphi),so i do not know JOB_RUN_PERIODICALLY const equal what.
if I get it i will tell you.And remember this program can only run on NT

good luck

hubdog
unit insjob;

interface

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

type
  TAT_INFO = record
    JobTime: DWord;
    DaysOfMonth: DWord;
    DaysOfWeek: UCHAR;
    Flags: UCHAR;
    Command: PWideChar;
  end;

  PAT_INFO = ^TAT_INFO;
  NET_API_STATUS = LongInt;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

function NetScheduleJobAdd(ServerName: PWideChar; Buffer: PAT_INFO; var JobID: PDWord): NET_API_STATUS;stdcall;

var
  Form1: TForm1;

implementation
{$R *.DFM}
function NetScheduleJobAdd; external 'netapi32.dll' name 'NetScheduleJobAdd';

procedure TForm1.Button1Click(Sender: TObject);
var
  ATInfo:PAT_Info;
  jobid:PDword;
begin
  getmem(atinfo,sizeof(TAt_info));
  getmem(jobid,sizeof(dword));
  atinfo^.jobtime:=3*60*60*1000+15*60*1000;//miliseconds from midnight to 3:15
  atinfo^.DaysOfMonth:=4294967295;
  atinfo^.DaysOfWeek:=255;
  atinfo^.command:='c:\showok.exe';
  atinfo^.flags:=1;//job_run_periodic


  if NetScheduleJobAdd(nil,atinfo,jobid)<>2 then//job_exec_error=2
    showmessage('ok');
  freemem(jobid);
  freemem(atinfo);
end;

end.
//this demo has been tested

good luck

hubdog
Jesus...

Just take a timer, interval 1 minute. check every minute if your internal clock matches hour:00 or hour:15 or hour:30 or whatever.

That's it. Small and easy.

HELLO!

TJ, your question and your last comment are inconsistent. Every half hour or only once a day!

Having problems checking the time from the tdatetime? Some string copying with pos should do the trick.

Your answer is here, in the comments.

Brunohe is right. It's getting over-complicated here without the need.

F.
Avatar of aj85

ASKER



Thanks for everyone's help.