Is it possible to have unit names added to each call to a function in a different unit? - delphi

One of the problems I often run into, is that I will include 'Windows' in my uses clause, and then I will later add 'JwaWinBase' for some specific calls.
However, many of the functions in the 'Windows' unit are the same as in JwaWinBase, and I start getting errors in my main unit, all over the place, until I fix all my calls by pre-pending the correct unit name, like this:
Old:
CreateProcessAsUser(...)
New:
Windows.CreateProcessAsUser(...)
JwaWinBase.CreateProcessAsUser(...)
What I want to know, is if there is a way to have the unit name automatically pre-pended to every call to a function in another unit? This way, before I added JwaWinBase to my uses clause, I could have the 'Windows' unit name pre-pended to any function calls. Then adding JwaWinBase wouldn't give me any errors.
I'm currently using Delphi 2007.

There isn't.
However, the function calls are handled in the reverse order they're in the uses clause, so that if you have this:
uses
Windows, JwaWinBase;
... it will call Jwa functions by default. However, if you reverse them:
uses
JwaWinBase, Windows;
... it should call the Windows functions by default, and you can preface your Jwa functions as required.

If you really need the resolution on a per-routine base, you could try inline forwarders:
procedure Blah; inline;
begin
Windows.Blah;
end;
procedure Blubb; inline;
begin
JwaWinBase.Blubb;
end;
// later:
procedure UseThem;
begin
Blah; // calls Windows.Blah
Blubb; // calls JwaWinBase.Blubb
end;
at the beginning of the implementation section (completely untested :-)).

Maybe it's enough to just switch the order of the two units in the uses clause.

If you use an editor like CodeRush (D7 and before) or Castalia or even the template feature of D2009, you can create templates that expand to what you want AS you enter them. Then you can keep the Windows, jwaWinBase order in the uses section. You can set jcpau to expand to jwaWinBase.CreateProcessAsUser while cpau expands to CreateProcessAsUser or to Windows.CreateProcessAsUser depending on your preference. You'd only have to go through the various functions in jwaWinBase and make templates for them to be safe.
Otherwise, I think it's search and replace on a case-by-case basis.

Related

Is it possible to use a library just for design time in delphi?

I am trying to write a component which is loading 3D objects from obj files.
I am using ToolsAPI library for GetActiveProject.FileName. I added designide.dcp to Requiers part in the bpl. I registered my object and in design when I put an instance of this object on a TViewPort3D which I put before everything is OK and I can see the object from the obj file is loaded in the scene, but when I try to compile the project I get an error that says ToolsAPI.dcu not found.
The procedure that I use for loading the obj file is (Type of Model variable is TModel3D) :
procedure TMyObject.LoadModel(fileName: string);
begin
if(csDesigning in ComponentState)then
Model.LoadFromFile(IncludeTrailingPathDelimiter(ExtractFilePath(GetActiveProject.FileName))+'Obj\'+filename)
else
Model.LoadFromFile(IncludeTrailingPathDelimiter(ExtractFilePath(ParamStr(0)))+'Obj\'+filename);
end;
This procedure is used in constructor as follow (TMyObject inherited from TDummy):
constructor TMyObject.Create(AOwner:TComponent)
begin
inherited;
Model:=TModel3D.Create(Self);
Model.Parent:=Self;
LoadModel('Object1.obj');
end;
Is there anyway to prevent using the ToolsAPI library when the host project for the component is about to compile?
I just thinking about something like directives as follow.
{$IFDEF DESIGNTIME}
uses ToolsAPI;
{$ENDIF}
But is it possible to do such a thing?
It sounds as though you are trying to compile the design time code into a run time project. Either a run time package, or an executable. That's not allowed. You simply cannot compile any of the ToolsAPI units into a project that is not a design time package.
You can certainly use conditional compilation to exclude the ToolsAPI units, but you will have to define your own conditional define. There is no built-in conditional that will serve your needs.
But using conditional compilation is probably not the best solution. Typically you would separate the code that used Tools API into distinct units, and only include those units in the design time projects.
So the code for your component would be split into, say, two units. The first unit, uMyComp.pas, say, contains the bulk of the code. This unit declares the component and provides its implementation. Nothing in uMyComp.pas makes any reference to ToolsAPI. The second unit, uMyCompReg.pas say, performs the component registration and any other tasks that require the ToolsAPI. There is a dependency between these units in that uMyCompReg.pas uses uMyComp.pas. Then your design time package will include both units, and any other projects that are not design time will include only uMyComp.pas.
You could achieve the same effect using conditionals. The design time project would define a conditional to indicate that this was design time. So the project settings might include a definition of a conditional named DESIGNTIME. Then all the code for your component would reside in a unit named uMyComp.pas, say. Any code related to design time would be conditional on DESIGNTIME. And any other projects that included uMyComp.pas would not have DESIGNTIME defined and so would omit the design time only code.
Whilst this is possible it is not, in my view, the best way to solve the problem. Indeed if you look around the wealth of open source examples of component development I'd be surprised if you found any that handled the separation of design time code from run time code using conditionals.
How would you separate the ToolsAPI code into a design time unit? Here's the problem method:
procedure TMyObject.LoadModel(fileName: string);
begin
if csDesigning in ComponentState then
Model.LoadFromFile(IncludeTrailingPathDelimiter(
ExtractFilePath(GetActiveProject.FileName))+'Obj\'+filename)
else
Model.LoadFromFile(IncludeTrailingPathDelimiter(
ExtractFilePath(ParamStr(0)))+'Obj\'+filename);
end;
First of all, let's look at the commonality of this code. The first think to observe is that the outsides of the call to LoadFromFile are the same. Only in the middle, the choice of directory, is there variation. So let's write it like this:
procedure TMyObject.LoadModel(fileName: string);
var
ModelDir: string;
begin
if csDesigning in ComponentState then
ModelDir := ExtractFilePath(GetActiveProject.FileName)
else
ModelDir := ExtractFilePath(ParamStr(0));
Model.LoadFromFile(IncludeTrailingPathDelimiter(ModelDir)+'Obj\'+filename);
end;
The problem for you is how to move GetActiveProject.FileName into the design time code. You need to use dependency injection (DI) to do this. Allow some other party to supply the logic. You need to make TMyObject ignorant of this particular detail. You could use a DI framework for this, but that's perhaps a little heavyweight just for this one task. So instead let's declare a class variable that holds a function pointer:
type
TMyObject = class(...)
...
public
class var GetModelDir: TFunc<string>;
end;
This function point allows other parties, external to the class, to specify how the model directory is located. Now LoadModel becomes:
procedure TMyObject.LoadModel(fileName: string);
var
ModelDir: string;
begin
if Assigned(GetModelDir) then
ModelDir := GetModelDir()
else
ModelDir := ExtractFilePath(ParamStr(0));
Model.LoadFromFile(IncludeTrailingPathDelimiter(ModelDir)+'Obj\'+filename);
end;
At this point, your code can now be used outside of a design time package. The next step is to add code to specify GetModelDir at design time. This code goes in the design time only unit that also registers the component. The obvious place for the code is in the initialization section of that unit. It looks like this:
initialization
TMyObject.GetModelDir :=
function: string
begin
Result := GetActiveProject.FileName;
end;
I've used anonymous methods here, but you could equally use method of object, or plain old functional types, depending on your Delphi version.
Yes, but preferably not with conditional defines as this would create far more complications and restrictions than it's worth.
You need to separate your code into different units according to whether it's design-time code or run-time code.
E.g. For a single component, the bulk of the (with no ToolsAPI dependency) goes into one unit.
A second unit performs component registration and perhaps provides custom design-time editors for the component.
The second unit uses the first and you have a clean separation without conditional defines.
You then create 2 separate packages: design-time and run-time.
The design time package will have a dependency on the ToolsAPI.
Make sure that none of the run-time units use any of the design-time units.
If any design-time units use run-time units (very likely) then the design-time package will require the run-time package.
With the above package structure, your application that uses your new components should only have dependencies on the run-time units.

How to register a component and property editor at run-time?

After much searching, it looks like I have to assign RegisterComponentsProc and RegisterPropertyEditorProc, which I have done.
However, I thought that I could call my design time Register function, i.e. <myComponentUnit>.Register();.
When I do I get stack overflow, because, well ...
procedure myComponentUnit.Regiter;
begin
RegisterPropertyEditor(TypeInfo(Integer),
TMyComponent, 'myProperty', TMyProperty);
end;
procedure RegisterPropertyEditor(PropertyType: PTypeInfo;
ComponentClass: TClass; const PropertyName: string;
EditorClass: TPropertyEditorClass);
begin
if Assigned(RegisterPropertyEditorProc) then
RegisterPropertyEditorProc(PropertyType, ComponentClass, PropertyName,
EditorClass);
end;
So, I call .Register();
which calls RegisterPropertyEditor()
which call RegisterPropertyEditorProc()
which calls RegisterPropertyEditor() <=== aaargh!!
So, what should I have in the body of my RegisterPropertyEditorProc ?
After further searching, it looks like I want to call DesignEditors.RegisterPropertyEditor() directly, but it is not in the interface section ...
There is no point in trying to register a property editor at run-time, as it is not usable at run-time to begin with. It is only usable within the IDE during design-time.
Delphi does not include the source for the DesignEditors unit; its implementation is provided solely in the DesignIDE package. That package has access to IDE internals, such as the list of registered property editors. The IDE assigns values to the RegisterComponentsProc and RegisterPropertyEditorProc callback functions. As you noticed, RegisterPropertyEditor calls RegisterPropertyEditorProc. The IDE provides its own function to handle that event.
If you want to register a property editor at run time, then your program plays the role of the IDE. You need to provide implementations for those callback functions to register the property-editor classes with your own property-editing framework. You could probably just keep everything in a simple list. Then, when you want to know what kind of editor to display for a certain type of property, consult the list to find the best match.
You're correct that you should call your units' Register procedures. But that's how you initiate the registration process, not how you implement it. That part's up to you; Delphi doesn't provide any of this for you.

Separating Interface and Implementation classes in delphi?

Am separating my delphi code into interface and implementation units ie.
EmployeeIntf.pas looks like this
type
// forward declaration
TScheduleList = class;
TDeparment = class;
TEmployee = class(BDObject)
....
function GetSchedules: TScheduleList;
function GetDepartment: TDepartment;
end;
TEmployeeList = class(DBList)
....
end;
TEmployeeDM = class(BDDBobject)
...
end;
Then i have the two units ScheduleIntf.pas & DepartmentIntf.pas which declare the TScheduleList class and TDepartment class.
Then in my main unit which combines all the units looks like this,
Unit BusinessDomain
Interface
uses
classes
{$I Interface\EmployeeIntf.pas}
{$I Interface\DepartmentIntf.pas}
{$I Interface\ScheduleIntf.pas}
Implementation
uses
SysUtils
{$I Implementation\EmployeeImpl.pas}
{$I Implementation\DepartmentImpl.pas}
{$I Implementation\ScheduleImpl.pas}
Initialization
finalization
end.
When i compile this the compiler throws an error;
*Type TScheduleList is not yet completely defined*
How can i have this classes separate in each unit file (.pas) and then do forward declarations without the compiler throwing this error?
The class themselvs are huge and i would prefer to separate them this way.
Gath
My first advice: Skip this $Include thing altogether. As Uwe wrote find a more Delphi-like solution.
If you really want to stay with the $Include style: The error you quote occurs because forward declarations don't work across "type" blocks. You forward declare TScheduleList in one block but define it in a different block. To cure this omit the "type" keyword in your *Intf.pas's and insert it in BusinessDomain.pas before the includes.
I'm afraid, there is no possibility to split a class declaration into multiple files. If the classes are that large, you should consider a redesign.
Another alternative are Interfaces:
type
IEmployee = interface
{ public properties and methods of an employee }
...
end;
type
TEmployee = class(BDObject, IEmployee)
...
end;
The interface and class declaration can now reside in different files.
Ulrich is quite correct (+1) whilst you can manipulate your include files to work approx this way, it's not going to be a very good Dev experience for you.
Think of the $Include directive as a simple text replacement mechanism, the things that make your units, well...units is the scoping mechanisms (uses sections, type sections etc) they will be much more difficult to use in an include file, thats why include files are usually called xxx.inc and not xxxx.pas as they often will not stand up as a source file by themselves. This of course makes them very difficult in a dev environment as they are then effectively just text files not proper debuggable units.
Include files are one of those legacy pascal features which keep rolling forward. There was a point when we didn't have a uses clause, and it was the only way to manage multiple files (of course we all programmed using wordstar commands for code navigation... wait, they are still there too).
Today, the most common use of include files is to include a block of code which must be shared among multiple files and can't be just used from another unit. As Mason pointed out in another comment, that would be the IFDEF-DEFINE blocks for determining things like what compiler options should be turned on, and what defines should be enabled project wide. We are no longer bound by the 64k limit on a source file.
Some other points to consider. Most of the tools for searching your source may not navigate into your include files. Using something as simple as a text search to find that text message that keeps popping up might be difficult. You would be much better served by not putting any code in them at all. When translating a map file to code, I believe your line numbers specified in the map file would be the total file if the include file was merged in place. If you use an automated tool such as MadExcept the error line reported might not be the actual location.
My suggestion would be to use interfaces as Uwe suggested. They are not just for COM, and can solve your desire to separate the "interface" from the "implementation".
Maybe my previous comment was a little presumptuous ... Reading your question again I think you may have misunderstood how to use units and in particular the "uses" directive.
You can declare individual classes both interface and implementation in a single unit file:
unit EmployeeDBCLassesU
uses system, DB, Blah, blah; // Units needed by this unit
interface
type
TEmployeeList = class(DBList)
Procedure DoSomething;
end;
TEmployeeDM = class(BDDBobject)
Procedure DoSomething;
end;
implementation
{TEmployeeList}
Procedure TEmployeeList.DoSomething;
begin
...
end;
{TEmployeeDM }
Procedure TEmployeeDM.DoSomething;
begin
...
end;
Then later to use them elsewehere:
Unit BusinessDomain
interface
uses EmployeeDBCLassesU; // MY units needed by this unit
.
.
.
This brings all the class definition in to BusinessDomain
and you can then do
TBusinessDomain = class(BDDBobject)
EmployeeList: TEmployeeList;
EmployeeDM: TEmployeeDM;
.
.
.;
end;
Hope this helps more as you will gain so much from the correct approach - you will realise this especially when navigating units for editing and Debugging.
If you really like to separate interface and implementation, have a look at Modula2. This is also a pascal like application, but it uses two files for each "unit". One for the interface and one for the implementation.
Another solution, is to split the files or class definitions, and write a custom preprocessor that links these (text wise) together. You then have something like:
unit BusinessDomain
interface
uses
classes;
type
Employment = class from Interface\EmployeeIntf.pas;
Department = class from Interface\DepartmentIntf.pas;
Schedule = class from Interface\ScheduleIntf.pas;
implementation
uses
SysUtils;
external define
Employment = class from Implementation\EmployeeImpl.pas;
Department = class from Implementation\DepartmentImpl.pas;
Schedule = class from Implementation\ScheduleImpl.pas;
end;
end.
With Delphi 2009 you can issue commands before and after the build phase. So technically this is possible.

How to access delphi function at DPR scope

I have a problem with maintenance on an old Delphi program (D7). A lot of the program logic is in the DPR file (this is not a windowed program), with some units providing things like access to the database. We need to get some debug out of the DB unit, but the debug functionality is in the DPR. We can't easily strip the debug functionality out, because it uses stuff that's unique to the DPR, like its main pipe. Separating it out would be like trying to tease apart spaghetti and meatball sauce.
So how do we call a function that's declared at the DPR scope from a subordinate used unit? What's the equivalent of the :: operator in C++ ?
Please don't tell me to redesign the app. I'd love to, but we won't be given the necessary time. Plus if we redesigned this puppy, it wouldn't be in Delphi.
You can declare a method variable in the unit that matches the signature of the function in the DPR. At the very beginning of the program you set the method variable to the function. Inside the unit you call the method variable.
Example:
(DPR)
uses
Unit1;
function DoSomething(Par: Integer): Integer;
begin
...
end;
...
begin
DoSomethingVar := DoSomething;
...
end;
(unit)
unit Unit1;
interface
...
var
DoSomethingVar: function(Par1: Integer): Integer;
...
implementation
...
SomeResult := DoSomethingVar(SomeParameter);
...
You can't. The unit hierarchy is rigid.
There are two possible options:
pull out the relevant parts of the .dpr to a new unit. Keep in mind that moving uses to the implementation can break import cycles. The createform* stuff probably isn't safe to move, that would probably upset the project manager.
or define a few callback functions (function,method types, like functionpointer in C), and move code out of the relevant unit initialization to a procedure that you call from the .dpr if necessary.
I don't know how to use functions from .dpr in other units, but if you have to change code simply change .dpr to normal unit and then use it's functions/routines in new .dpr and in others units.
.dpr is the most fundmental pascal project file.
We can use any unit files in the project, and so, you can use
the functions/procedures in the units.
If necessary, you can prefix the unit name for accessing the
function/procedure.
If the function/procedure belongs to specific class, you need
to create an instance to access the function/procedure because
it's a method.
Anyway, Delphi uses object pascal as its core, you can access
any necessary method/procedure/function/properties with legal
pascal concepts and object pascal concepts.
No matter which file you need to call it, and the only one except
is the decalaration file for some external library.

Is possible to include files (linking) based on a component property?

Delphi 2007/2009 odd question here:
It's possible, based on a component property defined in design-time, to include files in linking or leave them ?
Example: If I leave SomeProperty true, when compiling, the unit SomeUnit will be included into my project. Otherwise it will not be included.
My second approach to this problem is to deploy a second component, which when dropped in the form (or not) will include the unit in uses clause. But if it can be done with a property, that'll be better.
I want to avoid conditional compilation via IFDEF because that forces the component to be built every time the projects are built. Or not?
I am trying to achieve an easy way of including some units in project, and then those units will provide support for specific databases. Having these into an option, at the connection component, will be ideally easy: Check support and that's done. Uncheck, and get some less KBs in your compiled APP.
edit: I'll stay with the component way for instance. I knew the IFDEF method and things, but that forces the component to be built everytime the projects are built. Or not?
I was trying to achieve an easy way of including some units in project, and then that units will provide support for specific databases. Having these into an option, at the connection component, will be ideally easy: Check support and that's done. Uncheck, and get some less KBs in your compiled APP.
No.
What are you trying to solve?
You could add a postcompiling step that would optionally include some resource based on a component property - but you'd have to do some coding to implement such a feature.
You can use the {$IFDEF youridentifier} optional code {$ENDIF} method to conditionally compile data in to your application and then to enable it just go to your project options and enter youridentifier into the appropriate option field. Another method of doing this is to add the following to the top of your unit (or in an include file):
{$DEFINE youridentifier}
which will force youridentifier on. To disable, just place a period right before the $:
{.$DEFINE youridentifier}
Using these techniques its easy to conditionally bring in code or replace code with each compile.
Write an IDE add-in. Handle the "before compile" notification and check whether any forms or data modules in the project have components of the type you're interested in, and then check their properties. Based on what you find there, you can try modifying the contents of a unit to use the other unit of your choice. It certainly doesn't sound easy, but it seems possible.
Your second idea is very easy. It's exactly what the TXPManifest component does, for example. Beware that removing such a component from a form does not "unuse" the associated unit.
To conditionally add support for different databases, you might consider using run-time packages. (That's how the IDE manages to support so many different kinds of components, after all.) Put each database's custom code into a different package. Then, the databases you support are simply whichever ones have packages available at run time. No compile-time or design-time configuration required. The obstacle to this, however, is to manage which packages are available, and determine which of them are the packages that provide database support.
Your second approach will not necessarily work the way you want it to. While Delphi will helpfully add the necessary unit to your uses list when you drop the component onto the form, it will not remove the unit when you delete the component. And even if you don't use the component or any other exported entitiy from that unit, it is possible that the unit will be linked to your application anyway, when there is code in the initialization or finalization part of the unit. This is unfortunately very often the case, even when it would be possible to initialize stuff on-demand.
There is no way to do what you are asking, but something you might not be aware of is that units which are included in your uses list but never referenced will have a minimal impact on the size of your executable. The smart linker that is in Delphi does a very good job of removing code which is never used. If you are careful about your "optional unit" which is referenced by the component, and don't have any code in it which is executed globally (everything is self contained in a class or classes) then it shouldn't matter if it is in the uses clause and not used, or not in the uses clause at all.
This would easily allow you to do what I think your wanting to do, which would be to drop a component on a form which includes a unit that then can be linked to your application. Removing the component would have the effect of not linking in the unit. I believe, however, that any resources (for example forms or other items included by the $R directive) which are in the used unit would still be included in the executable.
You could use DesignIntf.RegisterSelectionEditor to register a selection editor (see comments in Delphi source code about ISelectionEditor), then use the RequiresUnits procedure to include extra units in uses clause.
TMySelectionEditor = class(TSelectionEditor)
public
procedure RequiresUnits(Proc: TGetStrProc); override;
end;
procedure Register;
implementation
procedure TMySelectionEditor.RequiresUnits(Proc: TGetStrProc);
var
comp: TMyComponent;
I: Integer;
begin
inherited RequiresUnits(Proc);
Proc('ExtraUnit');
// might be a better way of doing the code from here onwards?
if (Designer=nil)or(Designer.Root=nil) then Exit;
for I := 0 to Designer.Root.ComponentCount - 1 do
begin
if (Designer.Root.Components[i] is TMyComponent) then
begin
comp := TMyComponent(Designer.Root.Components[i]);
if comp.SampleProperty = True then
Proc('ExtraUnit2');
Proc(comp.ObjProperty.UnitName);
end;
end;
end;
procedure Register;
begin
RegisterSelectionEditor(TMyComponent, TMySelectionEditor);
end;

Resources