tags:

views:

504

answers:

2

Which physical file stores the main form name for a Delphi application?

e.g. MyApplication has a form MyForm which is set as the "Main form" via Project options. Where is the information "Main Form = MyForm" actually stored?

In the Delphi IDE the application "Main form" is specified via the menu: Project | Options | Forms.

The obvious file would be the .bdsproj or the .dpr but there doesn't seem to be anything in either of these that indicates which form is the "main" one.

+14  A: 

It's in the project (.DPR) file. The first call to Application.CreateForm() with a form as a parameter identifies the application's main form.

Note that a TDataModule doesn't satisfy the above requirement; that's actually useful, as you can autocreate a datamodule before your main form and then access that datamodule in the main form's constructor.

Ken White
I had memories of it being something to do with ordering somewhere. The trick is that it's the first CreateForm(TForm, ...). The app I'm looking at had the form creation buried among data module creations so it was non-obvious it was the first one. Thanks for the reminder :)
WileCau
Yeah, datamodules aren't eligible to be main forms (obviously), and they're excluded from the "first call" rule I mentioned. I'll edit my post to reflect that correction.
Ken White
@Ken: Why would autocreating data modules to access them in the constructor of the main form be better than creating them there?
mghie
@mghie: Maybe because creating datamodules in the mainform would make a unnecessary binding between the datamodules and the main form. It would sort of give the mainform the ownership of the datamodules.
Vegar
@Vegar: Doesn't matter whether the Application or the main form owns the data modules. And the binding is already there, since both Application object and main form are as top-level in the project as can be. That doesn't convince me at all. [Continued]
mghie
If you OTOH accept that globals are bad - how can it be a good thing that the Application owns the data modules? That one is forced to use the single global instances?
mghie
Wow! Sleep for a couple hours and I get hammered. <g> @mghie: Creating a DM isn't always instantaneous; if you autocreate, the user sees it as Windows being slow loading your app. If you create manually in FormCreate, your app is unresponsive and your user sees it as crappy. :-)
Ken White
@Vegar: mghie is right. Where you create the datamodule doesn't affect who owns it; it's how. If you use Application.CreateForm(), the application owns it; however, if you use DM := TDataModule.Create(Application), application *still* owns it. Changing it to Create(Self) makes another TComponent own
Ken White
(more) it. Creating it with Create(nil) means nobody owns it, and your own code is responsible for freeing it..
Ken White
@mghie: Re globals. I only have two globals ever max in my Delphi apps. THere's the main form's variable, which TApplication wants to have, and the var declared for the datamodule if there is a DM. Everything else is created locally as needed and assigned to local variables (or notthing at all).
Ken White
@Ken: There is actually no difference between auto creating the form in the DPR, and creating it manually in the constructor / OnCreate handler - as long as Visible is not manually set to True there the main form will be invisible in both cases. And it looks like Windows is to blame ;-)
mghie
And agreed with the global vars. Still, with several DMs and some of them possibly instantiated several times I tend to remove the DM vars completely. Let the main form create them and keep references to them in private fields.
mghie
+1 for avoiding globals when possible. Like I said, I get rid of all but a max of 2; I could probably do away with those, too, but I guess I'm just too lazy. <g>
Ken White
A: 

Just to add to Ken White's answer.

If you look at the source for CreateForm:

procedure TApplication.CreateForm(InstanceClass: TComponentClass; var Reference);
var
  Instance: TComponent;
begin
  Instance := TComponent(InstanceClass.NewInstance);
  TComponent(Reference) := Instance;
  try
    Instance.Create(Self);
  except
    TComponent(Reference) := nil;
    raise;
  end;
  if (FMainForm = nil) and (Instance is TForm) then
  begin
    TForm(Instance).HandleNeeded;
    FMainForm := TForm(Instance);
  end;
end;

You see that the function (despite its name) can be used to create other components. But only the first component that is a TForm and that is created succesfully, can be the main form.

And then a rant on global variables.

Yes globals are often wrong, but for an application object and a mainform object you can make an exception. Although you can omit the global for the mainform but you need to edit the dpr file yourself:

Change:

begin
  Application.Initialize;
  Application.CreateForm(TMyMainForm, MyMainFormGlobal);
  Application.Run
end.

To:

procedure CreateMain;
var
  mainform : TMyMainForm;
begin
  Application.CreateForm(TMyMainForm, mainform);
end;

begin
  Application.Initialize;
  CreateMain;
  Application.Run
end.

And you lost all global forms.

Gamecat
@Gamecat: Typo in your last block of code. You declared "mainform", but used the old "MyMainFormGlobal" in the CreateForm() call. You might want to fix that. <g>
Ken White
Gamecat
Are you sure that last code works? The IDE doesn't like it when code that LOOKS like it belongs in the main begin-end block appears elsewhere in the DPR file. It's hit-or-miss whether the IDE will allow you to compile the project if you make the wrong changes.
Rob Kennedy
Yes it works. (I tried it using 2006). I know the IDE doesn't like it, but it doesn't like other legal construct also. So as long as it compiles and runs there is no problem for me.
Gamecat