How do you deal with IFDEFs in .dpr uses section - delphi

Whenever you add a new unit to the project Delphi rebuilds the .dpr file and all the IFDEFs in the uses section are gone.
To work around this I typically use NotePad to create new .pas files, and add it to the .dpr manually. If I need a form I use File->New->Form and then revert the .dpr file to the previous version. Not very RAD if you ask me ;-)
How do you deal with that? Is there a way to add a unit in the IDE while keeping the IFDEFs?

Sometimes I create a unit specifically as a place for all the IFDEFs and other stuff the IDE would mess up if it were in the dpr. This unit typically goes to the top of the dpr's uses clause. This trick doesn't cater for all scenarios but it sometimes saves a lot of tedious work.

I don't put any ifdefs into a dpr file. If I want to use different units/forms in a project, depending on some condition, I split the project in two.

I spent quite a while trying to work that one out,
I ended up have a project file (.dpr) for each build type,
with the Conditions in Project|Project Options|Directories/Conditionals
and only the units i wanted added in to the project
this dose have the down side that if you have custom code in the .dpr, it will have to be manually copied to the other project files when it changes.
as noted by Rob Kennedy, this can handled by putting the custom code into its own unit, which is called by a single procedure. thus minimizing the .dpr code size/changes to be made
Also, another bonus you get is that if you add all your .dpr files to a project group, you can build all your different versions with one click / cmd line

You can add it manually from within the IDE. (Use the "view source" option on the project).
Normally the dpr is "hidden". You are not expected to change anything in there.
And if you do, you better make sure all your changes are manual else you are losing some information.

For forms, datamodules, and other units which contain a single class by which functionaity will be replaced, the solution is rather simple. Just DON'T add the custom units directly to the product, but do save them some place in the search path (or modify the project search path to include thier location).
1) Create a NEW unit, which contains either the parent for all of the other classes, or the interfaces that they all will implement (I generally prefer the later as it allows easier customization) [for example purposes this is called uSpecialParent.pas]
2) Add a class variable which will referenced when you need to create the new functionality. for instance if you just were going to show modal a bunch of forms, so didn't care about any other methods then you could have a variable that looked like the following:
TYPE
TMySpecialFormClass : class of TForm;
VAR
TMySpecialForm : TMySpecialFormClass;
3) Create another unit which will contain all of the IFDEFS. It could look something like the following:
Unit uRegisterSpecialForms;
interface
uses
{$IFDF SPECIAL1}
uSpecial1,
{$ENDIF}
{$IFDEF SPECIAL2}
uSpecial2,
{$ENDIF}
uSpecialParent;
implementation
// no code needed.
initialization
{$IFDEF SPECIAL1}
TMySpecialForm := uSpecial1.TSpecialForm1;
{$ENDIF}
{$IFDEF SPECIAL2}
TMySpecialForm := uSpecial2.TSPecialForm2;
{$ENDIF}
end.
4) To reference this in your code you only need the uSpecialParent added to the unit which will be requesting a special form and then create it dynamically for example to show this modal you could invoke the following:
var
frm : TForm;
begin
frm := TMySpecialForm.Create(nil);
try
frm.showmodal;
finally
frm.free;
end;
end;

And here's the lo-tech approach for completeness' sake:
After the IDE has messed up your uses clause again:
close the project
go to your version control tool of choice and diff the DPR against the latest checked-in
version using a merge-enabled diff tool like WinMerge
revert the IDE changes
save the DPR
get on with it

(Delphi 7)
I've just tried the same.
Take a look at the first code version and at my comments below:
program Project1;
{$IFDEF TESTIFDEF}
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {Form2};
{$ELSE}
uses
Forms,
Unit1 in 'Unit1.pas' {Form1};
{$ENDIF TESTIFDEF}
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TForm2, Form2);
Application.Run;
end.
At that point, I've just inserted the 2nd Form and noticed that the corresponding unit (Unit2.pas) was inserted inside the first part of the IFDEF i.e. inside the "TESTIFDEF" labeled part - hence not overriding the second block (after the {$ELSE}).
Thus your solution should be:
define a IFDEF statement like "{$IFDEF DELPHIBASISCONFIGURATION}" in place of my "{$IFDEF TESTIFDEF}" where all the forms will be added.
define as many alternative LABELS for the different configurations you want to work with.
each time you've added a form to the project, copy the inserted line of the first block into the corresponding blocks below - depending on your needs...
activate the required configuration using the define statement or the option dialog
NEVER DEFINE "DELPHIBASISCONFIGURATION" ;)
Hence, it should look like this:
program Project1;
{$DEFINE MYCONFIG1} // THIS ONE IS NOW ACTIVE
{$IFDEF DELPHIBASISCONFIGURATION}
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {Form2},
Unit3 in 'Unit3.pas' {Form3};
{$ELSE}
// THIS IS A "COMMON TO ALL CONFIG" PART
uses
Forms,
// FIRST CONFIGURATION
{$IFDEF MYCONFIG1}
Unit1 in 'Unit1.pas' {Form1},
Unit3 in 'Unit3.pas' {Form3}
{$ENDIF MYCONFIG1}
// SECOND CONFIGURATION
{$IFDEF MYCONFIG2}
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {Form2}
{$ENDIF MYCONFIG2}
// THIS IS THE "COMMON TO ALL CONFIG" END :)
;
{$ENDIF TESTIFDEF}
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TForm1, Form1);
//Application.CreateForm(TForm3, Form3);
//Application.CreateForm(TForm2, Form2);
Application.Run;
end.
As you can see, I've discarded the calls to Application.CreateForm(...) for Form2 and Form3.
IMHO, it's usually better to dynamically create the supplemental forms at the moment you really need them i.e. not all the forms at program start...

Related

Form appears after 'Application.CreateForm' step

I am working on a project in delphi 2007 (CodeGear RAD Studio).
There are couple of forms in the application. Thouse forms are created as follows:
program MyProgram;
uses
Forms,
uMain in 'Source\uMain.pas' {MainForm},
uSettings in 'Source\uSettings.pas' {fSettings};
{$R *.res}
begin
Application.Initialize;
Application.CreateForm(TMainForm, MainForm);
Application.CreateForm(TSettings, Settings);
Application.Run;
end.
the problem is, that on line Application.CreateForm(TSettings, Settings); Settings form appears (not modal). And the question is why it is happening?.
I know that it is probably not enough information, but I am ready to provide some, if it is needed.
P.S. I am currently re-wrighting programm logic so settings form will be created only before it is about to appear, and after that delete it. Still, I would like to know the answer to this question.
Your settings form's Visible property is set to true at design time, and thus it appears as soon as it's created.

What to NOT do to prevent Delphi from mangling the uses list and {$*.RES} in a .DPR

Every couple of weeks, I bump into this: when doing an IDE operation on the uses units in a Delphi project, it mangles the .dpr file.
What happens is that it rebuilds the uses list, but gets the position wrong.
I'm wondering what usage pattern to avoid so I won't get into this error again.
I've had this error occur in many Delphi versions. I know it exists in at least Delphi XE2 (it happened there again today), XE, 2007, 2006 and 7.
The mangled fragment usually is structured like this:
ususes
Forms,
..
LastUnitInUses in 'LastUnitInUses.pas';
R *.RES}
and should be corrected by removing one us, and adding a {$:
uses
Forms,
..
LastUnitInUses in 'LastUnitInUses.pas';
{R *.RES}
Example file that went wrong:
program SysUtilsFormatTests;
{
Delphi DUnit Test Project
-------------------------
This project contains the DUnit test framework and the GUI/Console test runners.
Add "CONSOLE_TESTRUNNER" to the conditional defines entry in the project options
to use the console test runner. Otherwise the GUI test runner will be used by
default.
}
{$IFDEF CONSOLE_TESTRUNNER}
{$APPTYPE CONSOLE}
{$ENDIF}
ususes
Forms,
TestFramework,
GUITestRunner,
TextTestRunner,
SysUtilsFormatUnit in 'SysUtilsFormatUnit.pas';
R *.RES}
begin
Application.Initialize;
if IsConsole then
with TextTestRunner.RunRegisteredTests do
Free
else
GUITestRunner.RunRegisteredTests;
end.
Example of corrected .dpr file:
program SysUtilsFormatTests;
{
Delphi DUnit Test Project
-------------------------
This project contains the DUnit test framework and the GUI/Console test runners.
Add "CONSOLE_TESTRUNNER" to the conditional defines entry in the project options
to use the console test runner. Otherwise the GUI test runner will be used by
default.
}
{$IFDEF CONSOLE_TESTRUNNER}
{$APPTYPE CONSOLE}
{$ENDIF}
uses
Forms,
TestFramework,
GUITestRunner,
TextTestRunner,
SysUtilsFormatUnit in 'SysUtilsFormatUnit.pas';
{$R *.RES}
begin
Application.Initialize;
if IsConsole then
with TextTestRunner.RunRegisteredTests do
Free
else
GUITestRunner.RunRegisteredTests;
end.
The only thing that I know that works is for you to let the IDE manage the .dpr file.
Don't add comments.
Don't use conditionals like $IFDEF.
Don't modify the code in the .dpr file.
if you do any of these things, expect the IDE to bite back.
Personally I do all of these and fight back at commit time. I use my VCS to defend against bogus IDE changes. It's not ideal, but it's the best option.

Why is this resource directive showing in the list of available forms?

I just stumbled on something very peculiar. Refer to this screenshot:
Why is it showing $R *.res in the list of available forms? This project only has two forms and one additional unit, and here's the project's main file source:
program MyProgram;
uses
Forms,
uMain in 'uMain.pas' {fMain},
uEmail in 'uEmail.pas' {frmEmail},
Vcl.Themes,
Vcl.Styles,
Other.Unit in 'Other.Unit.pas';
{$R *.res}
begin
Application.Initialize;
TStyleManager.TrySetStyle('Iceberg Classico');
Application.Title := 'My Program Title';
Application.CreateForm(TfMain, fMain);
Application.Run;
end.
PS - This is slightly modified code, the only thing that changed was the program name, program title, and the name of one of the units (Other.Unit.pas) which I know is a bad example since Unit is a reserved word. But the original unit name has a namespace prefix such as this one.
UPDATE
I followed the recommendation to move this RES reference to before the uses clause. After dong this (and cleaning all temp files, restarting IDE, etc.), it still shows in the list. But to my surprise, the IDE has actually added this RES reference back!
So now the IDE its self has turned it into:
UpdateUnit in 'UpdateUnit.pas' {$R *.res};
(UpdateUnit is a new name I gave it so I'm not giving internal information away)
So, I opened the DPROJ file and did a search for *.res and sure enough found this:
<ItemGroup>
....
<DCCReference Include="UpdateUnit.pas">
<Form>$R *.res</Form>
</DCCReference>
As written, I could not reproduce this... however if I removed the trailing semicolon (";") after the "Other.Unit in 'Other.unit.pas'" line, the "$R *.res" shows up in the project options.
This isn't a bug, as far as I can see. Since your comments indicate that the presented code isn't the same as what you're using it will be hard to diagnose. I will try and explain what is happening; If you look at the uMain and uEmail units, there is a comment next to each that is the name of the form. Since these units may not actually be open in the IDE, there is no way to know if there is a form associated with that unit. The presence of a uMain.dfm isn't a guarantee that uMain.pas actually has a form (it may be left-over cruft). The IDE places this simple comment within the uses clause for that unit in order to tell the project manager that this unit has a form.
By removing the ";", the parser sees the next comment token as {$R *.res}. It then concludes that Other.Unit.pas must contain a form named "$R *.res". It does no validation of the name nor even tries to open the unit. It merely takes the raw content of that comment and assumes it's a form name.
You can safely move the {$R *.res} before the uses clause so that the project parser doesn't confuse that directive comment as a form.
EDIT: From my comment above:
What has likely happened is this; at some point you may have edited the .dpr file in a manner that confused the parser into thinking that "$R *.res" was the form name. You then saved it while in that state, which also injected that information into the .dproj file. The IDE tries to keep those two things in sync and sometimes will take the .dproj file as being the "master" and will update the .dpr file to match. You should be able to safely remove the $R *.res from the dproj file.
This does not reproduce with Delphi XE2 Update 4 (see below).
More accurately: the code you posted does not reproduce.
But the scenario that you describe in the comments does: if the .dproj is hosed, the IDE will re-add the incorrect information to your .dpr. See below for further explanation.
At the end of this answer an edit that shows you an occurrence at a client: Delphi XE2 can get confused somehow to the internal state gets wrong, and it writes back both a wrong .dproj and .dpr file.
If the code you posted is not the same as the code that fails, please correct your question with the code that fails (and post a comment to my answer so I get a notification and update the QC entry; in the current form your QC entry will be marked as "cannot reproduce" and then closed).
I have seen the IDE being confused in various places (including the project options) after an AV, or when line-endings in the .pas or .dpr files are not CRLF, or when manually editing .DPR files.
Those issues usually disappear when you restart the IDE. Sometimes you even have to clean up some files (with extensions like .DCU, .local, etc).
Worst case is that the IDE got so confused that both the .DPR and .DPROJ contain wrong information (hence my comment), that appears to be the case in your situation (thanks to your edited question). You can manually edit the .DPROJ and .DPR files to resolve this. Make sure to have backups as it is easy to break the XML format of a .DPROJ file
I could reproduce it if I moved the RES declaration before the semicolon: that's the format the IDE expects so it can parse the .DPR for Forms, DataModules and Frames (and potentially other design surfaces):
Unit1 in 'Unit1.pas' {DataModule1: TDataModule},
Unit2 in 'Unit2.pas' {Frame2: TFrame};
Unit3 in 'Unit3.pas' {Form3};
Basically, you should not edit the .DPR file: the IDE owns it and will rewrite it for instance when you add new units to your project, you change the program icon, change the application caption, etc.
Main program that does not reproduce:
program MyProgram;
uses
Vcl.Forms,
uMain in 'uMain.pas' {fMain},
uEMail in 'uEMail.pas' {frmEmail},
Vcl.Themes,
Vcl.Styles,
Other.Module in 'Other.Module.pas';
{$R *.res}
begin
Application.Initialize;
TStyleManager.TrySetStyle('Iceberg Classico');
Application.Title := 'My Program Title';
Application.CreateForm(TfMain, fMain);
Application.Run;
end.
The options dialog:
Main program that does reproduce (note where the RES is):
program MyProgram;
uses
Vcl.Forms,
uMain in 'uMain.pas' {fMain},
uEMail in 'uEMail.pas' {frmEmail},
Vcl.Themes,
Vcl.Styles,
Other.Module in 'Other.Module.pas' {$R *.res};
begin
Application.Initialize;
TStyleManager.TrySetStyle('Iceberg Classico');
Application.Title := 'My Program Title';
Application.CreateForm(TfMain, fMain);
Application.Run;
end.
Edit 20130616
Ran into this at a client below is the diff of the before/after in both the .dpr and the .dproj (some of the names have been anonymized).
Delphi XE2 got confused somehow: it was not an edit in the .dpr that induced this problem.
There are two things I suspect:
an access violation somewhere overwrote some internal state causing the internal project structure to be damaged (the project depends on a lot of 3rd party packages to be installed, all of which are in the same BDS process)
there is lots of code in the .dpr that might have confused the internal project structure
I've refactored all of the .dpr code into a separate module. Hopefully that was the cause and this won't happen again.
Old .dpr fragment:
program Server;
uses
ShareMem,
SysUtils,
Forms,
SvcMgr,
WebReq,
uMain in 'uMain.pas' {fMain},
//...
uDBOrm in 'uDBOrm.pas',
uWinProxySettings in '..\..\..\Server\trunk\Server\uWinProxySettings.pas',
GpStuff in 'GpStuff.pas',
DSiWin32 in 'DSiWin32.pas';
{$R *.res}
function IsServiceApp: Boolean;
begin
//...
end;
begin
if IsServiceApp = True then
begin
//...
end else
begin // GUI Interface
if Assigned(Application) then
FreeAndNil(Application);
Forms.Application.Initialize;
Forms.Application.MainFormOnTaskbar := True;
ReportMemoryLeaksOnShutdown := True;
Forms.Application.CreateForm(TfMain, fMain);
Forms.Application.Run;
end;
end.
New .dpr fragment:
program Server;
uses
ShareMem,
SysUtils,
Forms,
SvcMgr,
WebReq,
uMain in 'uMain.pas' {fMain},
//...
uDBOrm in 'uDBOrm.pas' {$R *.res},
uWinProxySettings in '..\..\..\Server\trunk\Server\uWinProxySettings.pas',
GpStuff in 'GpStuff.pas',
DSiWin32 in 'DSiWin32.pas';
{$R *.res}
function IsServiceApp: Boolean;
begin
//...
end;
begin
if IsServiceApp = True then
begin
//...
end else
begin // GUI Interface
if Assigned(Application) then
FreeAndNil(Application);
Forms.Application.Initialize;
Forms.Application.MainFormOnTaskbar := True;
ReportMemoryLeaksOnShutdown := True;
Forms.Application.CreateForm(TfMain, fMain);
Forms.Application.Run;
end;
end.
Old .dproj fragment:
<DCCReference Include="uDBOrm.pas"/>
<DCCReference Include="..\..\..\Server\trunk\Server\uWinProxySettings.pas"/>
New .dproj fragment:
<DCCReference Include="uDBOrm.pas">
<Form>$R *.res</Form>
</DCCReference>
<DCCReference Include="..\..\..\Server\trunk\Server\uWinProxySettings.pas"/>

Why main form can't access to DataModule's images and actions when project is open in Delphi XE?

My Delphi XE application was fine till a couple of days and I can't figure out what is wrong. My project layout:
Visual controls are on the main form
Actions and image lists for those controls are on a data module
When I open my project, the main form doesn't have any image or actions associated to any of the controls, even though they should be. When I compile I get the error message: "Module 'winMain' links to module 'modGeneral' which cannot be found in the current project. Do you wish to remove/redirect the links to another module?".
The work around: close the main form after I've opened the project, then open the module in the IDE by double-clicking it in the project manager (yes, it is present in the current project), then re-open the main form: all my actions and images are now correctly displayed.
What do you think, is that a known Delphi bug ? A problem with my project ?
Check your .dpr file. One way to reproduce your problem is to change the uses clause in it.
Consider this example which works fine:
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas' {DataModule2: TDataModule};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TDataModule2, DataModule2);
Application.Run;
end.
The icon in the Project Manager looks as usual for a module with a dfm:
If you remove the comment, or comment it out:
program Project1;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
Unit2 in 'Unit2.pas'; // {DataModule2: TDataModule};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.CreateForm(TDataModule2, DataModule2);
Application.Run;
end.
Here, the icon has changed:
...and you get the errors you describe.
You need to close and reopen the project for your changes to take effect.
TOndrej's answer is complete and correct. I have just a small addition which I maybe should post as comment but I'm afraid it won't be noticeable.
I faced with the described error after unit renaming and project file refactoring (actually I removed all the in and comments from uses section). I returned the required pattern in the uses section but I still got the error. My mistake was an alignment that I added to comments in uses section:
DMMain in 'DMMain.pas' {fdmMain : TDataModule},
DMIndex in 'DMIndex.pas' {fdmIndex : TDataModule},
Surprisingly it really matters. Removing the alignment
DMMain in 'DMMain.pas' {fdmMain: TDataModule},
DMIndex in 'DMIndex.pas' {fdmIndex: TDataModule},
fixed the error and I got things working.

Delphi 7 make complains about files not found

I've got a BPG file that I've modified to use as a make file for our company's automated build server. In order to get it to work I had to change
Uses * Uses
unit1 in 'unit1.pas' * unit1
unit2 in 'unit2.pas' * unit2
... * ...
in the DPR file to get it to work without the compiler giving me some guff about unit1.pas not found.
This is annoying because I want to use a BPG file to actually see the stuff in my project and every time I add a new unit, it auto-jacks that in 'unitx.pas' into my DPR file.
I'm running make -f [then some options], the DPR's that I'm compiling are not in the same directory as the make file, but I'm not certain that this matters. Everything compiles fine as long as the in 'unit1.pas is removed.
It could come from the fact, that the search path in the IDE and the search path of the command line compiler are not the same. If you change the serach path of the command line compiler you might be able to use the exactely same source code as within the IDE.
One possibility to configure the search path for the command-line compiler is to do it in a file called dcc32.cfg. Take a look at the help, there is a short description of dcc32.cfg in the IDE-help.
Well this work-around worked for me.
//{$define PACKAGE}
{$ifdef PACKAGE}
uses
unit1 in 'unit1.pas'
unit2 in 'unit2.pas'
...
{$else}
uses
unit1
unit2
...
{$endif}
The only problem is whenever you add a new unit, delphi erases your ifdef package at the top.
Every time I have to put conditionals into a project file I do this:
program a;
uses
ACondUnits;
...
unit ACondUnits;
interface
uses
{$IFDEF UseD7MM}
Delphi7MM;
{$ELSE}
FastMM4;
{$ENDIF}
implementation
end.
Maybe this trick works in packages too. Never tried.

Resources