Sunday, June 20, 2010

Is Application Object a window in Delphi?

Often sometimes while working in Delphi I wondered whether the Application object itself is a window or not.. and if so then where exactly is this window and why is it not visible? Going through Marco Cantu's Mastering Delphi book I found that yes infact Application object in delphi is infact a window but than this fact drove me into curiosity to just see this window visibly and hence I thought of programming a bit on this fact. Though Marco suggested that the best way to see that "Application object is infact a window" is to run Delphi, select "New Application" which creates a new windows application and gives Form1 as a default form and then simply press the Run button. Now on looking into the task bar we are see that the title for our new application is getting displayed as Project1 and not Form1 which clearly suggests that there is infact a hidden window in our newly created windows application. This hidden window is none other than very own Application window but still if we could see the window well stretched on our desktop then it would be a better confirmation to this fact after all to see it clearly is better than mearly realising a fact... Am I Correct? So here we go..

Now to start with - first here is a bit of delphi lecture - If we will look at how TApplication has been defined in Delphi then we find that the class has been inherited from TComponent class but than on going through the class definition we find that Application object infact allows programmer to have access to its handle value.. Handle in itself is a unique 32bit (for 32 bit systems) number that windows OS assigns to each window that will be handling any sort of windows messaging.
Once while I was going through some VC++ lessons and VB programming I once read that if we have access to the handle value fo a window we can manipulate that window to the maximum extent via the windows API's and hence I plan here to do the same to make the "Application Object" show its coordinates and make itself visible...
(Please note that according to Delphi standards all the visible components etc are derived from TControl or TWinControl and not from TComponent and hence if TApplication is invisible to us then it is pretty much cool with us.. but this program is just to show that a delphi program is actually having two windows running at the same time.. one the master which is TApplication and the other is the MainForm..).

Now no more lectures and we are back to work...
So here is a small program to serve our purpose -
First of all lets start Delphi and create this simple form -

(Please click on the image to enlarge)


and on the OnCreate event of the form1 we will try to display the coordinates of the form and Application object via the use of the API GetWindowRect. The GetWindowRect API is used to find the coordinates of a window as screen coordinates for which a handle number is known. So our API statement will look similar to something like -
GetWindowRect(Form1.Handle, FormRect); for form1 and similarly GetWindowRect(Application.Handle, AppRect); for Application object window, where FormRect and AppRect are of type TRect.
The procedure I have written to do the following and display the same in the edit boxes in the form is as follows -
procedure TForm1.RefreshRectInfo;
var
FormRect : TRect;
AppRect : TRect;
begin
GetWindowRect(Form1.Handle, FormRect);
edtTop.Text := IntToStr(FormRect.Top);
edtLeft.Text := IntToStr(FormRect.Left);
edtRight.Text := IntToStr(FormRect.Right);
edtBottom.Text := IntToStr(FormRect.Bottom);
GetWindowRect(Application.Handle, AppRect);
edtAppTop.Text := IntToStr(AppRect.Top);
edtAppLeft.Text := IntToStr(AppRect.Left);
edtAppRight.Text := IntToStr(AppRect.Right);
edtAppBottom.Text := IntToStr(AppRect.Bottom);
end;
Once the form gets opened up, the user is shown that there are infact two different handles assigned to Application and Form1 and hence suggesting that Application object would be handling different messages and is seperate from form1 though is not visible to us. Also, just check what the Application Windows Coordinates group box values are displaying. For me these values suggested the following -> Top: 384, Left: 683: Right: 683 and Bottom: 384. What does this mean? Well this mean that Application window is actually a ZERO size window on the desktop and hence we are not able to see it. So to make it visible we will be just trying to stretch it a little bit not enough to tear it but enough to make it visible to us... :) ..
Now, when the user clicks on the button - "View Application Window" the Application window will become visible however as the form1 which is the MainForm still remains visible hence the Application window remains behind the form1 window. See the screenshot for the same below -

(Please click on the image to enlarge)


As you can see the values displayed in the Application Window coordinates groupbox of form1 have also changed for the Application window and the application window is getting displayed with a border though it displays a transparent client area.

To remove the form1 window just check the checkbox Hide form1 and the application window will be displayed when the user clicks on the View Application Window button. However keep this in mind that here we can't change the actual behaviour of how delphi treats its MainForm and as we made the MainForm invisible hence the once we minimize the Application Window the MainForm will remain invisible and also the Application Window.
Thus by this small exercise we have been able to show that Application window is actually a window with size Zero and is working behind the scenes.. :) ..

Thats all for now.. Here is the dfm code and the pas file code which I created for this application in Delphi 5.0 -
DFM Code (AppWindow) -
object Form1: TForm1
Left = 192
Top = 122
Width = 506
Height = 232
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object lblAppObjHandle: TLabel
Left = 16
Top = 8
Width = 129
Height = 13
Caption = 'Application Object Handle: '
end
object lblFormHandle: TLabel
Left = 16
Top = 32
Width = 72
Height = 13
Caption = 'Form1 Handle: '
end
object edtAppObjHandle: TEdit
Left = 176
Top = 8
Width = 121
Height = 21
ReadOnly = True
TabOrder = 0
end
object edtFormHandle: TEdit
Left = 176
Top = 32
Width = 121
Height = 21
ReadOnly = True
TabOrder = 1
end
object btnViewAppWindow: TButton
Left = 312
Top = 5
Width = 145
Height = 25
Caption = 'View Application Window'
Default = True
TabOrder = 2
OnClick = btnViewAppWindowClick
end
object gbRectCoordinates: TGroupBox
Left = 0
Top = 61
Width = 490
Height = 135
Align = alBottom
Caption = 'Windows Coordinates'
TabOrder = 3
object gbForm1Coordinates: TGroupBox
Left = 2
Top = 15
Width = 239
Height = 118
Align = alLeft
Caption = 'Form1 Coordinates'
TabOrder = 0
object lblTop: TLabel
Left = 8
Top = 24
Width = 25
Height = 13
Caption = 'Top: '
end
object lblLeft: TLabel
Left = 9
Top = 47
Width = 24
Height = 13
Caption = 'Left: '
end
object lblRight: TLabel
Left = 8
Top = 67
Width = 31
Height = 13
Caption = 'Right: '
end
object lblBottom: TLabel
Left = 8
Top = 88
Width = 39
Height = 13
Caption = 'Bottom: '
end
object edtTop: TEdit
Left = 72
Top = 20
Width = 49
Height = 21
ReadOnly = True
TabOrder = 0
end
object edtLeft: TEdit
Left = 72
Top = 42
Width = 49
Height = 21
ReadOnly = True
TabOrder = 1
end
object edtRight: TEdit
Left = 72
Top = 64
Width = 49
Height = 21
ReadOnly = True
TabOrder = 2
end
object edtBottom: TEdit
Left = 72
Top = 86
Width = 49
Height = 21
ReadOnly = True
TabOrder = 3
end
end
object gbAppWindowCoordinates: TGroupBox
Left = 241
Top = 15
Width = 247
Height = 118
Align = alClient
Caption = 'Application Window Coordinates'
TabOrder = 1
object lblAppTop: TLabel
Left = 8
Top = 24
Width = 25
Height = 13
Caption = 'Top: '
end
object lblAppLeft: TLabel
Left = 9
Top = 47
Width = 24
Height = 13
Caption = 'Left: '
end
object lblAppRight: TLabel
Left = 8
Top = 67
Width = 31
Height = 13
Caption = 'Right: '
end
object lblAppBottom: TLabel
Left = 8
Top = 88
Width = 39
Height = 13
Caption = 'Bottom: '
end
object edtAppTop: TEdit
Left = 72
Top = 20
Width = 49
Height = 21
ReadOnly = True
TabOrder = 0
end
object edtAppLeft: TEdit
Left = 72
Top = 42
Width = 49
Height = 21
ReadOnly = True
TabOrder = 1
end
object edtAppRight: TEdit
Left = 72
Top = 64
Width = 49
Height = 21
ReadOnly = True
TabOrder = 2
end
object edtAppBottom: TEdit
Left = 72
Top = 86
Width = 49
Height = 21
ReadOnly = True
TabOrder = 3
end
end
end
object cbHideForm1: TCheckBox
Left = 313
Top = 35
Width = 152
Height = 17
Caption = 'Hide Form1'
TabOrder = 4
end
end
Pas Code -
unit AppWindow;

interface

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

type
TForm1 = class(TForm)
lblAppObjHandle: TLabel;
lblFormHandle: TLabel;
edtAppObjHandle: TEdit;
edtFormHandle: TEdit;
btnViewAppWindow: TButton;
gbRectCoordinates: TGroupBox;
gbForm1Coordinates: TGroupBox;
gbAppWindowCoordinates: TGroupBox;
lblTop: TLabel;
lblLeft: TLabel;
lblRight: TLabel;
lblBottom: TLabel;
edtTop: TEdit;
edtLeft: TEdit;
edtRight: TEdit;
edtBottom: TEdit;
lblAppTop: TLabel;
edtAppTop: TEdit;
lblAppLeft: TLabel;
edtAppLeft: TEdit;
lblAppRight: TLabel;
edtAppRight: TEdit;
lblAppBottom: TLabel;
edtAppBottom: TEdit;
cbHideForm1: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure btnViewAppWindowClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure RefreshRectInfo;
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
begin
edtAppObjHandle.Text := IntToStr(Application.Handle);
edtFormHandle.Text := IntToStr(Form1.Handle);
RefreshRectInfo;
end;

procedure TForm1.btnViewAppWindowClick(Sender: TObject);
begin
SetWindowPos(Application.Handle, HWND_TOPMOST, 10, 10, Screen.Width - 100, Screen.Height - 100, SWP_SHOWWINDOW);
if cbHideForm1.Checked then
ShowWindow(Form1.Handle, SW_HIDE)
else
RefreshRectInfo;
end;

procedure TForm1.RefreshRectInfo;
var
FormRect : TRect;
AppRect : TRect;
begin
GetWindowRect(Form1.Handle, FormRect);
edtTop.Text := IntToStr(FormRect.Top);
edtLeft.Text := IntToStr(FormRect.Left);
edtRight.Text := IntToStr(FormRect.Right);
edtBottom.Text := IntToStr(FormRect.Bottom);
GetWindowRect(Application.Handle, AppRect);
edtAppTop.Text := IntToStr(AppRect.Top);
edtAppLeft.Text := IntToStr(AppRect.Left);
edtAppRight.Text := IntToStr(AppRect.Right);
edtAppBottom.Text := IntToStr(AppRect.Bottom);
end;

end.

No comments: