?
Solved

event handlers and formless apps

Posted on 2001-06-20
17
Medium Priority
?
390 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
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
 
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 200 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

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
Visualize your data even better in Access queries. Given a date and a value, this lesson shows how to compare that value with the previous value, calculate the difference, and display a circle if the value is the same, an up triangle if it increased…
Have you created a query with information for a calendar? ... and then, abra-cadabra, the calendar is done?! I am going to show you how to make that happen. Visualize your data!  ... really see it To use the code to create a calendar from a q…
Suggested Courses

650 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