Solved

event handlers and formless apps

Posted on 2001-06-20
17
366 Views
Last Modified: 2010-04-06
Hi,
How should i setup an event handler when not using forms.

here's what i've tried
======================
program Testapp;
uses blah, blah;

procedure beeperTimer;
begin
 beep;
end;

begin
  beeper := TTimer.Create(nil);
  with beeper do begin
   OnTimer := beeperTimer;
   Enabled := True;
  end;
end.
==========
The problem is it creates the timer then just closes. I want it to keep going until I choose to close it.


cheers


0
Comment
Question by:campid
  • 6
  • 5
  • 3
  • +2
17 Comments
 
LVL 13

Expert Comment

by:Epsylon
ID: 6212536
You can't use events because there is no message handler. This is also the reason why your app exits immediately.
And how do you want to close the app when there is no form?

The code below does basically what you are trying to do. It loops forever though.


program Project1;

uses
  Windows, SysUtils;

begin
  while true do
  begin
    Sleep(1000);
    Beep;
  end;
end.
0
 
LVL 1

Expert Comment

by:edsteele
ID: 6212581
Your problem here is that the default application shell that is created for a windowed program takes care of this for you.  It looks like this:

program MyProgramName;

uses
  Forms,
  MainU in 'MainU.pas' {MainForm};

{$R *.RES}

begin
  Application.Initialize;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

You can see this by clicking Project -> View Source.  This bit of code that Delphi creates for you keeps the application running until the main form in the program exits.

You are creating a console application.  This type of application has no such code given to you.  If you have some trigger for ending the program, you just need to put that into a loop in the body of the program.

Try this:

program Console;
{$APPTYPE CONSOLE}
uses
  SysUtils, ExtCtrls, Forms, Classes;

type
  TMyTimer = class(TTimer)
  public
    constructor Create(AOwner: TComponent); override;
    procedure MyOnTimer(Sender: TObject);
  end;

var
  ShouldExit: Boolean;
  Counter: Integer;
  beeper: TMyTimer;

{ TMyTimer }

constructor TMyTimer.Create(AOwner: TComponent);
begin
  inherited;
  OnTimer := MyOnTimer;
end;

procedure TMyTimer.MyOnTimer(Sender: TObject);
begin
  Beep;
  Inc(Counter);
  ShouldExit := (Counter = 5);
end;

{ Main Program }

begin
  Counter := 0;
  beeper := TMyTimer.Create(Nil);
  beeper.Enabled := True;

  while not ShouldExit do
  begin
    Application.ProcessMessages;
  end;
end.

This should give you 5 beeps, then exit.  I had to create to subclass the TTimer class so I could get the event inside the object.  Then in the Create constructor, I assigned your new event code to the OnTimer event.

Good Luck!
Eric
0
 
LVL 1

Expert Comment

by:Greyman
ID: 6212585
Epsylon is right - without a main form, your application will not stay active.

To declare and connect an event handler without using the form designer, do something like this:

TEventClass = class
  procedure HandleTimer(Sender : TObject);
end;


//Other declarations
var
  EventClass :TEventClass;
  Timer : TTimer;

procedure CreateAndHookupTimer;
begin
  EventClass := TEventClass.Create;
  Timer := TTimer.Create(nil);
  Timer.OnTimer := EventClass.HandleTimer;
end;

The key thing to note is that HandleTimer is a method of a class (this gives it access to the hidden Self parameter), and it has a declared input parameter of type TObject.  This means that HandleTimer conforms to the type TNotifyEvent, the default event handling procedure type.

(Note that some other events have a different parameter list, eg TDataSetNotifyEvent)
0
 
LVL 20

Expert Comment

by:Madshi
ID: 6213108
If you link Forms.pas into your project (like Eric suggested) your application will be quite large. If you want to have a as small as possible exe file, you should avoid using any VCL units, if possible even SysUtils.
If you want your program to stay alive, you have to do something. A program that has come to the "end." gets terminated. Usually one is using a message loop to keep a program alive - which is btw exactly what Application.Run does, too. And timers need a message loop anyway, so that is the way to go (unless you're satisfied with Epsylon's first suggestion).
However, you should NOT use Application.ProcessMessages in a loop, because then your CPU runs at 100% all the time!! Better use a message loop like this:

procedure MessageLoop;
var msg : TMsg;
begin
  while true do begin
    WaitMessage;
    if GetMessage(msg, 0, 0, 0) then begin
      TranslateMessage(msg);
      DispatchMessage(msg);
    end;
  end;
end;

About events in a formless app: You can either use the win32 APIs SetTimer/KillTimer (Delphi's TTimer is in fact an encapsulation of this API). Then you don't need this object stuff. Or you can look here:

http://help.madshi.net/Data/ProcToMethod.htm

Regards, Madshi.
0
 
LVL 1

Expert Comment

by:edsteele
ID: 6214060
Size is a relative thing.  My example came out at just under 300K.  Is that large?  It depends on who you ask.  I am showing our friend, campid, that exactly what he is asking for can be done.  It is also one of the easiest ways without having to delve into the realm of Windows messaging.

Madshi is right that the Application.ProcessMessages will spike your CPU usage.  Another option is to use Application.HandleMessage instead.  The timer still executes right on time and the CPU doesn't budge.

Eric
0
 

Author Comment

by:campid
ID: 6214194
Hi guys thanks for all the response.

i've used Madshi's last example to stop the program from closing which then works fine. But.... I have to include the extctrls.dcu file to get the timer function. This then makes the program triple in size to 300kb. I have heard you can just include the functions you use but how is this done?

I also noticed that im only using the Sysutils.dcu for the IntToStr function.

Here are the includes im using;
Windows, extctrls, classes, Sysutils;

cheers
0
 

Author Comment

by:campid
ID: 6214394
Hi guys thanks for all the response.

i've used Madshi's last example to stop the program from closing which then works fine. But.... I have to include the extctrls.dcu file to get the timer function. This then makes the program triple in size to 300kb. I have heard you can just include the functions you use but how is this done?

I also noticed that im only using the Sysutils.dcu for the IntToStr function.

Here are the includes im using;
Windows, extctrls, classes, Sysutils;

cheers
0
 
LVL 20

Expert Comment

by:Madshi
ID: 6214752
The unit "Windows" doesn't add anything to your exe size, but the other units do. Especially "extctrls". So if size is important for you, you should use the SetTimer/KillTimer APIs instead of TTimer. If you also get rid of classes and SysUtils, your exe can be as small as 16kb.

Regards, Madshi.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 20

Expert Comment

by:Madshi
ID: 6214761
P.S: Of course size is relative. 300kb for a full-blown application is nothing. But sometimes it's nice to have a 20kb application only, e.g. when writing a self-extractor or something like that. It depends on the purpose, of course...
0
 

Author Comment

by:campid
ID: 6214899
Madshi,

Please could you give an example of how to use SetTimer/KillTimer APIs, then my problem will be solved.

Cheers
0
 
LVL 1

Expert Comment

by:edsteele
ID: 6215023
I was also under the impression that you could "strip" all unused code from a final exe.  Is this already done and the exe is still 300K?  Or is there a flag or a process to strip unused code from the final product?

I remember using the "strip" command in school to bring the size of a program down considerably.  This was under Unix and done to C programs, so maybe there isn't a Borland or Windows equivalent.
0
 
LVL 20

Accepted Solution

by:
Madshi earned 50 total points
ID: 6215340
Delphi's linker already has integrated such a "strip" command automatically, it's named "smart linking", but as soon as you include any VCL unit, Delphi is lost, because they're all cross-linked and they all have a mega-sized initialization part, so Delphi simply can't link more out of the exe...   :-(

Here is an example for SetTimer/KillTimer:

procedure TimerProc(window, msg, eventID, time: dword); stdcall;
begin
  // here do whatever you like, this is the timer callback
end;

var timerID : dword;
begin
  timerID := SetTimer(0, 0, 2000, @TimerProc);  // Installs a 2 sec timer
  KillTimer(0, timerID);  // and kills it again...

Regards, Madshi.
0
 

Author Comment

by:campid
ID: 6215706
Cheers Madshi for all your help, heres your well earned points.
Oh where can I find more about api's and how to use them? and any idea where I can get the 'pas' include files from as D5 only comes with the 'dcu' versions.


Thanks again.
0
 
LVL 20

Expert Comment

by:Madshi
ID: 6215724
The source files are included in the professional edition and higher, not in the standard edition. Learning about APIs? Look at the win32.help file (must be somewhere in your Delphi directory).

Regards, Madshi.
0
 

Author Comment

by:campid
ID: 6215818
ah, I only have access to the enterprise version. any idea where i can get a copy of them?

cheers
0
 
LVL 1

Expert Comment

by:Greyman
ID: 6216540
Enterprise is higher than professional - look in Program Files/Borland/Delphi/Source
0
 
LVL 20

Expert Comment

by:Madshi
ID: 6217518
Right, and if you don't find them, maybe you didn't install them? Then update your installation...
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Suggested Solutions

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.

744 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now