Include different form (.dfm) file based on a conditional define - delphi

We produce two versions of our software for two slightly different versions of machine. The hardware used on the two machines is sufficiently different that we maintain two projects, Project1 and Project2. Some code (.pas and .dfm, as appropriate) is shared between the two projects, but most code is currently unique to Project1 or Project2.
Project2 was initially cloned from Project1 to get the new machine up-and-running, but now I'm in the process of restructuring the code so forms and units can be made common between the two projects to reduce development effort. Conditional defines, as in {$IFDEF PROJ1}, are used where required.
We have one form that is almost identical between the projects, apart from one TCheckBox which is in Project1 but not Project2. Lets call this form Masking with unit file Masking.pas (and Masking.dfm).
Steps:
Moved the .pas and .dfm from Project1 into the common shared folder
Renamed Masking.dfm to MaskingProj1.dfm
Copied Masking.dfm from Project2 into the common shared folder and renamed to MaskingProj2.dfm
Manually edited the project (.dpr) files for each project to reflect the changes
In Masking.pas, changed {$R *.dfm} into:
{$IFDEF PROJECT1}
{$R MaskingProj1.dfm}
{$ELSE}
{$R MaskingProj2.dfm}
{$ENDIF}
Also in Masking.pas, used the {$IFDEF PROJECT1} conditional define to compile out the TCheckBox control and associated code for Project2.
Finally, re-open both projects and re-compile. Hey presto! it works. Now Project1 uses Masking.pas and MaskingProj1.dfm. Project2 uses Masking.pas and MaskingProj2.dfm.
This all appears to works fine...except that when I try and view the form in the IDE, by switching between form and unit (F12), nothing is displayed. I can manually edit either .dfm file and the changes are reflected in the application after recompilation...but the IDE won't show the form.
What am I doing wrong? Is it possible to change {$R *.dfm} in this way to get the .pas file to use a different form file, depending on a conditional define?

This is a typical case for form inheritance.
Create a common ancestor form containing all controls needed for both of the projects. Now inherit this form for Project1 and add those controls needed only for Project1. Then do the same for Project2 (probably no controls to add here).
Both projects contain the common form, but each project contains only that inherited form suitable for said project.
Although you can omit the inherited form for Project2 here, I suggest to do it this way for clarity.

Related

Delphi {$INCLUDE filename} in uses part of dpr file

I have many Delphi 10 projects that are using the same units, let's call them "commons".
When I add anew unit to commons, I have to manually add it to each project. I have tried adding a {$INCLUDE commons.inc} line into the uses part of each .dpr file:
uses
Forms,
{$INCLUDE commons.inc}
projectUnit1,
...;
commons.inc has this content:
common1,
common2,
I can compile a project but cannot manage the units from commons.inc. By manage, I mean Ctrl-F12, remove from project, etc.
This is from Delphi's help:
There is one restriction to the use of include files: an include file can't be specified in the middle of a statement part. In fact, all statements between the begin and end of a statement part must exist in the same source file.
I suppose that is why my idea does not work?
Am I doing something wrong, or is there another solution?
This workaround might suit. The only downside I have found so far is that the included files do not appear in the Project Manager.
Add the folder(s) containing the files to be included to the search path of every project.
Create Include.pas, a normal .pas file, and include it in the normal way in every project.
Add the files to be included in multiple projects to the uses clause of Include.pas. $IFDEFS can be used if required.

How to use templates defined in other package without adding source file to the project?

I've created a package which contains several templates (TFrame descendants).
These templates has been added to the palette.
In other packages, I'm trying to use these templates but each time I add one of them, a message appear:
The following unit: %s, is needed in your project to create the
template. Do you wish to add it?
mrYes:
Source file is added to the project.
Furthermore, it causes the following error on compiling:
E2200 Package '%s' already contains unit '%s'
mrNo:
It's ok, but the question is asked each time I place one of my
templates. Me and my collegues will use these templates alot of times and I really would like to avoid that message.
Update 1:
Here are steps to observe the described behavior:
Create a package project named Package1.
Add a TFrame descendant named MyTest.
Add TMyTest to palette (Structure window, right click, add to
palette).
Set project output directories to .\out
Build the project.
Add \out to library path.
In the same projectgroup, create a package project named Package2.
Add Package1, to "requires" (in Package2.dpk).
Add a form.
Add a TMyTestTemplate to the form.
Update 2:
I'm looking for a solution which works with Delphi 2007 and Delphi XE7.
A bit more work but one that should fit your needs:
Create Package FooRuntime(Mark RuntimeOnly), add your TMyFrame
Create Package FooDesignTime(Mark DesigntimeOnly), requires FooRuntime, Registers your frame
Install FooDesignTime
Link your other project to FooRuntime
Without the packages the IDE seems to fail to recognize the units.

Referencing a type from a package library in Delphi

I have inherited a legacy Delphi project for which I have to do a code modification. But to start with, I have problem compiling the project into a "correct" application. As I am not familiar with Delphi, I am not sure what is missing.
So far, the only changes I have done are around PATH for references and packages wherever I could find an absolute path that was different on my PC. It is compiling and running. However, one of the variable remains "incorrectly" initialized at run-time (value is nil). I am a bit surprised because I have not changed anything in the code, that is from an application otherwise running.
The type of this variable is defined in a package library (for which I have the code as well, that compiles, and that is in the list of "Packages" checked in the Project Options.
Some code sample:
Unit Unit3
interface
uses
VCLZ;
TFrmCCB = class(TForm)
vz: TVCLZ;
implementation
procedure TFrmCCB.FormCreate(Sender: TObject)
var
[...]
begin
vz.SetPath := 'C:\'; //at this point vz is nil
Do I have to add some code to get vz to not be nil, or does it look like a configuration problem with the definition not found at run time or something like that? I would seem strange that something is missing in the code though, as I have not done any change there yet and the program is otherwise running.
It sounds like vz: TVCLZ is a component on a form.
When you first open the form if you don't have the component (package) installed in the IDE it asks you if you want to remove the component (because the IDE doesn't know how to render/instantiate it at design-time). If you choose to remove it the code associated with that component remains.
So you remain with something similar to what you have. The form doesn't create the component at run-time (because it doesn't have it anymore) the vz member remains uninitialized.
So, you have to get the sources of the project with the component on the form and be careful not to delete it.
Or, you care create the instance yourself. At FormCreate just create it: vz := TVCLZ.Create(Self); before you use it. But keep in mind that all the properties set in design will be lost and you have to set them manually in code.

GExpert's Replace Components Expert doesn't replace the text in the form file (*.dfm)

I am using Delphi 2010 and GExperts stable release 1.35
I am testing the "Replace Components" expert.
I add a main form and a secondary form. Each has three TEdits on it.
I use the Replace Components Expert to replace all TEdits with TRzEdit. I Check the Replace all components on all forms of the project.
It works, it replaces them all. However, it doesn't do anything to the DFM file. How do i make it replace those instances.
If i save, compile, or rebuild, i get errors.
If i try to view the form as text after replacing, i get errors.
Can someone explain to me the steps to make this work?
Thanks
I just tried it with Delphi XE and GExperts 1.35 and it does indeed crash now even after a single "Replace Selected". (It used to work fine in the past).
It seems that using the hidden menu Project | Clear Unit Cache just after Replace Components then doing a full build before trying any Save/Compile/View As Text... fixes the problem.
I think this menu is surfaced with cnPack. I don't have it and cannot guarantee, but you can easily do it yourself by adding the following unit in one of your installed packages:
unit FGEnableHiddenMenus;
interface
procedure Register;
implementation
uses
Classes, Forms, Menus;
procedure Register;
var
Comp: TComponent;
begin
//Make a hidden menu item visible
Comp := Application.MainForm.FindComponent('ProjectClearUnitCacheItem');
if Comp is TMenuItem then
TMenuItem(Comp).Visible := True;
end;
end.
Source: Brian Long's old goodies
Update:
I had to replace a couple of TEdit by TDBEdit on our main project at work and this trick worked.
But on a new Project with 3 Forms, it failed consistently to write/commit/save the changes on the last Form (same environment).
FYI, I tried with and without DDevExtensions 2.5 and IDEFixPack 4.6.1
Update2:
Went digging in the GExperts forum as suggested by Ulrich and finally found a possible explanation. The new property Touch does not like being copied from one component to another when the source is destroyed (causing the AV).
The suggested workaround is to do a bidirectional mapping in the Expert Settings to disable the copy for this property:
You might be running into this bug while trying to copy the Touch property from the old to the new component, but it has a workaround you can fairly easily test:
http://tech.groups.yahoo.com/group/GExpertsDiscuss/message/3994
Details:
There is a limitation/bug in Delphi 2010 and XE where if you assign a
Component.Touch property from one component to another and destroy the
original component that the new component becomes corrupt (it isn't
like component/interface references, where they either auto-correct
themselves or are reference counted).
For the moment, you can assign a bi-directional replace component
property map from TPanel.Touch to TGroupBox.Touch (use the two components being replaced in your specific case) that is marked as
a disabled property map, and that will work around this problem. Our next release
will not try to assign that property any longer.
GExperts 1.36 is also now available and includes a workaround for this issue. The workaround has been in the GExperts version control system and in testing for several weeks already.

Delphi: Prevent method names from appearing in executables

I am writing a class to handle security in my executable (checking serials, trial date check etc). After I compile the executable (even in Release build, with all debug and RTTI generation turned off), when I open it in NotePad and search the method name in the raw data, I can see all the names of the methods that assemble my class. There are no published members in any class in the code base.
This is bad for protection. Is there any way to tell Delphi not to store method names in the executable ? Why is it storing them at all if there is no RTTI needed and no COM explosion? Is there any compiler option controlling this?
It may be that ANY method of ANY class in the target executable is stored inside the executable in text form. Apparently this is caused by the extended RTTI being turned on by default for all classes in Delphi 2010.
If you are asking about the extended RTTI in Delphi 2010, it can be switched off by
{$RTTI EXPLICIT METHODS([]) PROPERTIES([]) FIELDS([])}
see also docwiki.
Also strip relocations, take up the following in the project's dpr file:
{$IFDEF RELEASE}
// Leave out Relocation Table in Release version
{$SetPEFlags IMAGE_FILE_RELOCS_STRIPPED}
{$ENDIF RELEASE}
... and don't forget to turn off "td 32 debug info" (in older versions) or debug info in the linker tab in later ones.
What you probably will see is your form definition as a resource (eg the binary represetation of the DFM files of your project).
If you don't want to show these (for the serial info screen etc) you shouldcreate these forms "in code". Eg create a TForm, place a TButton and TEdit onto it, attach the event handlers in code.
To do this in a handly way: start with a form and create the DFM. When vieing the form, choose View as text from the context menu and you will know what things you should copy into code. And make sure NOT to place any varaiablerefernces under de published (always put public/protected/private as the first line within your class definition.

Resources