Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

System Tray Icon Without Using Forms Unit?

Status
Not open for further replies.

ajbufort

Programmer
Apr 4, 2004
50
US
Hello All,

I am trying to write a program which has no forms, but places a small icon in the system tray. All of the examples I am seeing regarding system tray icons and Delphi are for apps using the FORMS unit. I do not wish to include this unit unless I absolutely have to, for size considerations. I want it to remain a PROGRAM.

How might this be accomplished?

Thanks,

-Tony
 
Just curious - what sort of functionality would the program have when you left/right-click the system tray icon?

Clive [infinity]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"To err is human, but to really foul things up you need a computer."
Paul Ehrlich
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To get the best answers from this forum see: faq102-5096
 
If you only want to show an icon in the system tray (like give a warning, you don't need a form, but if you want user interaction (clicking on the icon) you really need a form since the messaging mechanism uses window handles...

--------------------------------------
What You See Is What You Get
 
Are you going to allow users to change these settings, if so how are you planning to do this without a form?

Clive [infinity]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"To err is human, but to really foul things up you need a computer."
Paul Ehrlich
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To get the best answers from this forum see: faq102-5096
 

Furthermore:

Other than resorting to a totally non-VCL application you are gaining little or nothing taking out "Forms" from your visible "uses" clauses.

TApplication is defined in "Forms", so you need to drop it if you want to stay out from "Forms"; some other units uses "Controls", which may be is worse than "Forms" exe-size-wise. The only way to stay out of "Controls" is, probably, staying out of any VCL control.

A non-VCL GUI application is possible if you resort only to Win windows and controls. Another beast is if it is worth.

buho (A).

 
Hi Guys,

Yes, that is exactly what i am looking at doing - developing a totally non-VCL app. I only have a PROGRAM now, no UNITs. The WIn-only code looked beastly - that's why I was coming to you guys - to see if it had to be that way if one weren't using FORMS. I think, however, that I have come to terms with this, and am going to put back FORMS and a few other units, handle the system tray the easy way, and then use ASPACK to compress my executable. I have heard that there are some drawbacks to using an exe compressor, but it seems thsee are primarily related to multiple instances being prwsent on a machine - something which this app does not allow or provide for. Already ran some tests, and it shrunk my exe from 508K to about 250K.

-Tony
 
I did come across a webpage on the net that discussed how to have a Delphi program use forms without the Forms unit. It was beyond my brain to follow but you might search about for it.

Django
 
This link may be of use to you if you are going the forms route:
System Tray Delphi application

I was gonna suggest compression as an alternative. You might be interested in this page which asks a similar question to your own:
How to reduce delphi EXE size???

Clive [infinity]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"To err is human, but to really foul things up you need a computer."
Paul Ehrlich
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To get the best answers from this forum see: faq102-5096
 
ajbufort,

if you wan't to make really small applications in delphi (with forms) I would recommend the key objects library.

I used it several times in some games I made.

here's the link :

--------------------------------------
What You See Is What You Get
 
Thanks for all the feedback, guys! I have decided to go with the non-Forms option (decided it a while ago, actually), and came up with some very cool, relatively simple Win-api code, which I intend to share shortly. But now I have one last issue that is bugging me. I have the systray stuff working independently of the app I want to add this functionality to. But when I go to integrate, things don't behave as I expect. Maybe you can help me out.

I have the following test code:

Code:
program tt;

uses
  Dialogs, Classes;

type
  TMyThread = class(TThread)
  protected
    procedure Execute; override;
  end;

var
  SysTrayThread: TMyThread;
  x: integer;

procedure TMyThread.Execute;
begin
  ShowMessage('HERE');
end;

begin
  SysTrayThread := TMyThread.Create(True);
  SysTrayThread.FreeOnTerminate := True;
  SysTrayThread.Resume;

  x := 0;

  while x <> 2 do
  begin
    //
  end;
end.

I would expect that I would see a window with the message 'HERE', but I do not. What's going on? The program is still running, but my thread never seems to execute.

-Tony
 
Okay, I totally see what the problem was regarding my integration issues - I had to create the empty window necessary for the processing of sys tray menu messages WITHIN the thread itself. Like this:

Code:
procedure TMyThread.Execute;
begin
  // Place system tray icon in tray
  wndclass.cbSize	:= sizeof(wndclass);
  wndclass.style := CS_HREDRAW or CS_VREDRAW;
  wndclass.lpfnWndProc := @WndProc;
  wndclass.cbClsExtra	:= 0;
  wndclass.cbWndExtra	:= 0;
  wndclass.hInstance := hInstance;
  wndclass.hIcon := LoadIcon(hInstance, 'MAINICON');
  wndclass.hCursor := LoadCursor(0, IDC_ARROW);
  wndclass.hbrBackground := COLOR_WINDOW;
  wndclass.lpszMenuName	:= nil;
  wndclass.lpszClassName := strAppName;
  wndclass.hIconSm := LoadIcon(hInstance, 'MAINICON');

  RegisterClassEx(wndclass);

  // Create window for systray to relate to
  wnd := CreateWindow(strAppName, strAppName,
		         WS_CLIPSIBLINGS or WS_POPUP or WS_OVERLAPPED or WS_SYSMENU or WS_CAPTION,
             GetSystemMetrics(SM_CXSCREEN) div 2 - (cWidth div 2),
		         GetSystemMetrics(SM_CYSCREEN) div 2 - (cHeight div 2),
		         cWidth, cHeight, 0, 0, hInstance, nil);

  ShowWindow(wnd, SW_HIDE);

  while GetMessage(mess, HWND(nil), 0, 0) do
    begin
      TranslateMessage(mess);
      DispatchMessage(mess);
    end;
end; (* TMyThread.Execute *)

Works fine now.

BUT... I still don't understand why the example I gave before does not result in a 'HERE' message.

-Tony
 

> BUT... I still don't understand why the example
> I gave before does not result in a 'HERE' message.

You are breaking so many rules with it that is difficult to see what one (or ones) are causing the behavior.

Some of the most evident: a) you are using Dialogs but you have not a instatiated TApplication running; b) you have not a main form and c) you have not the message pumping engine running.

"A" y "b" are related to Delphi; "c" is related to Win itself.

IMHO, the reason is simple: your "tt" program is NOT a Win application.

buho (A).
 
Buho,

That's an interesting analysis, because when I simply replace my 'while' loop with another 'ShowMessage', everything works as expected!!

This would seem to indicate that:

a) I do not need TApplication explicitly instantiated;
b) I do not need a main form, and
c) I don't need the message pumping engine running.

-Tony
 

No idea, then. :)

By chance the working version lacks the thread?

Say, something like this:

Code:
begin
 ShowMessage('HERE');
end.

buho (A).
 
Buho,

No, I said that the working version replaces the while loop with a simple ShowMessage, like this:

This is the working version:

Code:
program tt;

uses
  Dialogs, Classes;

type
  TMyThread = class(TThread)
  protected
    procedure Execute; override;
  end;

var
  SysTrayThread: TMyThread;

procedure TMyThread.Execute;
begin
  ShowMessage('THREAD');
end;

begin
  SysTrayThread := TMyThread.Create(True);
  SysTrayThread.FreeOnTerminate := True;
  SysTrayThread.Resume;

  ShowMessage('MAIN');
end.

-Tony
 
Well... threads are not created with a message queue by default. In a queueless thread all messages are lost.

May be your new main thread ShowMessage is creating the queue now. Queues are created when Win detects the thread is asking for messages (using PeekMessage or GetMessage or the like).

New theory, then: your first program was not having main thread message queue and the second one have. So now the WM_xxx messages sent by Win to the dialog are not going astray.

buho (A).
 
I'm revisiting your code, only out of curiosity.

Please, considere this:

Code:
program MsgPump;

uses
  Windows, Dialogs, Classes;

type
  TMyThread = class(TThread)
  protected
    procedure Execute; override;
  end;

var
  SysTrayThread: TMyThread;

procedure TMyThread.Execute;
begin
  ShowMessage('THREAD');
end;

procedure CaseOne;
begin
  while True do;
end;

procedure CaseTwo;
begin
    ShowMessage('MAIN');
end;

procedure CaseThree;
var
  Msg : TMsg;
begin
  while GetMessage(Msg, 0, 0, 0) do
    begin
      TranslateMessage(Msg);
      DispatchMessage(Msg);
    end;
end;

begin
  SysTrayThread := TMyThread.Create(True);
  SysTrayThread.FreeOnTerminate := True;
  SysTrayThread.Resume;


  // ----------------------------------------
  // Only one "Case" uncommented at any time.
  // ----------------------------------------
  //CaseOne;
  //CaseTwo;
  CaseThree;

end.

Cases Two and Three appears to work; case One not.

It is something about the main thread message pump, that is for sure.

buho (A).
 
Buho,

Great. I had a theory (but did not know the specifics to cache it out) that the presence of another ShowMessage kicked something into gear which allowed the other to be processed and displayed. Those 'specifics' obviously involve the message queue.

-Tony
 
AJ-

In your original code, replace the while loop with a sleep(10000) and see if it works....

JGS
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top