Proper way to use forms with the same name - delphi

I have a project that has two forms that have the same name. I need to use one or the other. I assumed I could use an IFDEF to differentiate between them but I find I can't add both of them into the project without the compiler complaining. My uses clause looks like this.
uses
uFileManager, uMount, uSkyMap, uAntenna,
{$IFDEF SDR}
uSDR,
{$ENDIF}
{$IFDEF R7000Serial}
uR7000,
{$ENDIF}
uDatabase;
{$R *.dfm}
Both the 'uSDR' and the 'uR7000' units have a form named 'Receiver' in them. When I attempt to add 'uR7000' unit the project I get:
"The project already contains a form or module named Receiver"
How can I add both units to a project?

First of all, it's not the compiler complaining but the IDE.
The compiler doesn't care if you have forms - or other types - with the same name as long as they are in different units. A famous example from the VCL was the presence of two TBitmap types, one in Graphics the other in Windows. If you ever need to be explicit as to which type you mean, you simply qualify the type name in the code and the compiler does as it's told.
bmpA: Graphics.TBitmap; // bmpA is a TBitmap as defined in the Graphics unit
bmpB: Windows.TBitmap; // bmpB is a TBitmap as defined in the Windows unit
No problem there.
However, the persistence framework in Delphi does care if you have persistent classes with the same name since the persistence framework identifies types only by their unqualified name.
This is why every third party component framework for Delphi uses a prefix on their class names. It's not just vanity or fashion. It ensures that a component in one library cannot be confused (by Delphi persistence mechanisms) with another from a different library if both libraries are used in the same project.
Bottom line: Stick to unique names for your forms and find some other way to distinguish or switch between them if/as needed.
Precisely how to then go about managing your reference to which particular form is in use is difficult to suggest without further details about your project. You might derive both from a common base class or you might define an interface for each to implement.
For example (and this is just an illustrative sketch, not a recommendation or fully worked solution):
// Define the interface that your Receiver implementations
// must satisfy. This might include returning a reference to the implementing form.
//
// e.g. in a unit "uiReceiver"
type
IReceiver = interface
function Form: TForm; // returns the form using the common base type, not the specific implementation class
end;
// in unit uSDR
TfrmSDRReceiver = class(TForm, IReceiver)
..implements IReceiver as well as your SDR specific needs
end;
// in unit u7000
TfrmR7000SerialReceiver = class(TForm, IReceiver)
..implements IReceiver as well as your R7000 Serial specific needs
end;
// In uReceiver (some unit to "resolve" the receiver)
interface
uses
uSDR,
uR7000;
type
TReceiver = class
class function GetReceiver: IReceiver;
end;
implementation
class function TReceiver.GetReceiver: IReceiver;
begin
{$ifdef SDR}
result := frmSDRReceiver;
{$endif}
{$ifdef R7000}
result := frmR7000SerialReceiver;
{$endif}
end;
end.
Your application code then uses the uReceiver unit (and uiReceiver if you want to refer to the interface type, e.g. in a variable declaration) and accesses the particular Receiver implementation through the static class provided, e.g.:
uses
uReceiver;
implementation
uses
uiReceiver;
..
var
rcvr: IReceiver;
begin
rcvr := TReceiver.GetReceiver;
rcvr.... // work with your receiver through the methods/properties on the interface
// You can also work with the receiver form, accessing all aspects
// common to any TForm via the Form function on the interface (assuming
// you chose to provide one):
rcvr.Form.Show;
..
end;

Related

Cannot set enum type in published property of a component

I am writing a component and this is the main class with the most important pieces of code:
uses
Equation;
type
TEquationSolver = class(TComponent)
private
FSolver: TSolverType;
published
property RootFindAlgorithm: TSolverType read FSolver write FSolver;
end;
In the uses clauses I've added Equation because inside Equation.pas I have declared this kind of enum:
type
TSolverType = (TNewtonRaphson = 0, TSecant, TBisection, TBrent);
In this way I am able to have in the IDE an option in the Object Inspector with a dropdown menu.
I have installed the component and while I was testing I've found this problem:
procedure TForm1.Button1Click(Sender: TObject);
begin
EquationSolver1.RootFindAlgorithm := TSolverType.Secant;
end;
The error is the following:
[dcc32 Error] Unit1.pas(29): E2003 Undeclared identifier:
'TSolverType'
My question is very simple: why?
In the Unit where I am running the test (simple VCL form) there is the component with its uses included and so I am able to "see" TEquationSolver. As you can see at the top inside the unit of TEquationSolver I have included Equation and the latter has TSolverType.
The situation is the following:
Do I have to add something under the uses somewhere? I don't want to add stuff to the uses of Unit1.
If you want to make TSolverType visible to a unit (eg a Form), you must tell that unit where TSolverType is defined. That is part of how Delphi works.
Therefore, you have to either:
include Equation in the uses clause of the unit where you want to us the definition (eg the Form's unit)
include TSolverType in your component's unit
hide the property (eg by making it private or protected).
Delphi does not support implied definitions in the way you hope.

What is the correct way to export an object in Delphi? [duplicate]

Is it possible to put some classes into a DLL?
I have several custom classes in a project I am working on and would like to have them put in a DLL and then accessed in the main application when needed, plus if they are in a DLL I can reuse these classes in other projects if I need to.
I found this link: http://www.delphipages.com/forum/showthread.php?t=84394 which discusses accessing classes in a DLL and it mentions delegating to a class-type property but I could not find any further information on this in the Delphi help or online.
Is there any reason I should not put classes in a DLL, and if it is ok is there a better way of doing it then in the example from the link above?
Thanks
It is not possible to get a Class/Instance from a DLL.
Instead of the class you can hand over an interface to the class.
Below you find a simple example
// The Interface-Deklaration for Main and DLL
unit StringFunctions_IntfU;
interface
type
IStringFunctions = interface
['{240B567B-E619-48E4-8CDA-F6A722F44A71}']
function CopyStr( const AStr : WideString; Index, Count : Integer ) : WideString;
end;
implementation
end.
The simple DLL
library StringFunctions;
uses
StringFunctions_IntfU; // use Interface-Deklaration
{$R *.res}
type
TStringFunctions = class( TInterfacedObject, IStringFunctions )
protected
function CopyStr( const AStr : WideString; Index : Integer; Count : Integer ) : WideString;
end;
{ TStringFunctions }
function TStringFunctions.CopyStr( const AStr : WideString; Index, Count : Integer ) : WideString;
begin
Result := Copy( AStr, Index, Count );
end;
function GetStringFunctions : IStringFunctions; stdcall; export;
begin
Result := TStringFunctions.Create;
end;
exports
GetStringFunctions;
begin
end.
And now the simple Main Program
uses
StringFunctions_IntfU; // use Interface-Deklaration
// Static link to external function
function GetStringFunctions : IStringFunctions; stdcall; external 'StringFunctions.dll' name 'GetStringFunctions';
procedure TMainView.Button1Click( Sender : TObject );
begin
Label1.Caption := GetStringFunctions.CopyStr( Edit1.Text, 1, 5 );
end;
Use runtime packages for this purpose; it's exactly what they're designed for in the first place. They get loaded automatically (or can be loaded manually), and automatically set up the sharing of the same memory manager so you can freely use classes and types between them.
You're much better off using packages (which is exactly what the IDE does for much of its functionality for that very reason).
Delphi does not support either importing or exporting classes from DLLs. To import a class from another module, you need to use packages.
While the official answer is "you can't", anything is possible of course. Frameworks like Remobjects SDK and Remobjects Hydra has been doing this for a long time. The problem is that it requires you to create an infrastructure around such a system, which is not something Delphi deals with out of the box.
The first step is memory management. A DLL is injected into the process loading it, but it does not share memory management. It has to be this way since a DLL can be created in a myriad of languages, each with their own internal mechanisms. This poses a problem with safety (i.e program writing into DLL memory and visa versa).
Secondly, interface (read: content description). How will your application know what classes it can create, class members, parameter types and so on. This is why COM requires type-libraries, which describe the content of a DLL.
Third, life-time management. If memory management for the objects created from a DLL is handled by the DLL, the DLL must also release said objects.
The above steps already exists and it's called COM. You are of course free to create as many COM DLL files as you please, just remember that these have to be registered with Windows before you use them. Either "on the fly" by your application (if you have the security rights to do so) or by your installer.
This is why COM, while representing the ultimate plugin system, is rarely used by Delphi programmers, because the technical cost of using it as a plugin system outweighs the benefits.
The alternative way
If you can assure that your DLL's are only to be used by Delphi programs, then you have a second way to explore. You have to create methods to share the memory manager of your main program with the DLL (Remobjects does this). This allows you to share objects, strings and more between the DLL and the main application.
You can then use RTTI to "map" classes stored in the DLL (the DLL must do this and generate both class and method tables) which can be invoked through a proxy class you device yourself.
All in all, unless you have plenty of free time to waste, I would either buy a system like Remobjects Hydra - or stick with packages. But can it be done another way? Of course it can. But at the cost of time and hard work.

Delphi: declare variable avoiding circular reference

I've got a Delphi unit which needs to keep the pointer of various forms of the application, to do operations on them later.
In order to do those operations, I need to cast the pointer to a form type, ex.
var
ptrFrmMain: Pointer;
CurrentFrmMain: TfrmMain;
begin
CurrentFrmMain := ptrFrmMain;
CurrentFrmMain.Close();
end;
The problem is that this unit is contained in the uses of all the other Delphi units of the application. So while I can declare a simple Pointer type in the interface section, I cannot declare a type declared in the other units (such as TfrmMain of the unit frmMain.pas).
I could solve this by placing a use in the implementation section, such as:
interface
type TMyThread = class(TThread)
Public
ptrFrmMain:Pointer
...
implementation
uses frmMain
var
CurrentFrmMain: TfrmMain;
but there is still a problem: I need the variable to be specific to my class instance, for multithread purposes, and not a generic global variable.
But I cannot place it inside my TmyThread class, since TfrmMain is not declared there and I cannot place it in the uses of the interface section.
A solution would be to place CurrentFrmMain as a local variable in all the procedures which use it and then do the CurrentFrmMain := ptrFrmMain conversion each time, but do you know a better solution?
Thank you very much in advance.
I wouldn't put a Form pointer in the thread at all. I would have the thread hold callback functions instead, or even an interface:
type
TCloseProc: procedure of object;
TMyThread = class(TThread)
public
CloseProc: TCloseProc;
...
end;
...
begin
if Assigned(CloseProc) then CloseProc();
end;
type
IMyIntf = interface(IInterface)
['{9CC7DB9E-D47F-4B7D-BBF9-6E9B80823086}']
procedure DoClose;
end;
TMyThread = class(TThread)
public
Intf: IMyIntf;
...
end;
...
begin
if Assigned(Intf) then Intf.DoClose();
end;
...
type
TfrmMain = class(TForm, IMyIntf)
public
procedure doClose;
end;
procedure TfrmMain.doClose;
begin
Close;
end;
When the thread is created, assign the Form methods to those callbacks, or pass the Form's interface implementation to the thread:
Thread := TMyThread.Create(True);
Thread.CloseProc := frmMain.Close;
Thread.Resume;
Thread := TMyThread.Create(True);
Thread.Intf := frmMain as IMyIntf;
Thread.Resume;
Either way, the thread doesn't need to know about the actual Forms at all while still catering to Form-specific functionality.
Depends upon what do you mean by "keep the pointer of various forms of the application, to do operations on them later." - what kind (or kinds) of work that is? This is a question about generic software design, about decomposition, not just circular reference or any other language-specific issue.
If all you want to do is making same work over any form - then you should derive your forms from the same BASE-FORM-CLASS and keep references to that base class, not to the specific form classes. For example if you just need to .Release them you can just keep them all as TForm type reference which they all are derived from. This is just a typical case of extracting common abstract interface.
TMyFormWithActions = class ( TForm ) .... end;
TMyForm1234 = class ( TMyFormWithActions ) .... end;
TMyFormABCD = class ( TMyFormWithActions ) .... end;
You can also extract the common functionality not into intermediate class, but into the MS COM interface like Remy shown in his answer. This however is bordering with quite different memory model (ARC one) MS COM was based upon. While I do not expect TForm have auto-destroy reference counting, I also am not totally sure it can't happen, especially in inherited and complex application. So while I do like that approach, I omitted it because sometimes in practice it might cause unexpected and premature death of objects. If you can ensure that would not happen though it might be the most clean solution.
And if you need to do DIFFERENT actions, then you can indeed not merely store references to forms themselves, but also to actions, to software snippets. Then your thread-declaring class would build a general framework to keep forms-and-procedures data cells. And then you would have extra units implementing those specific actions to be passed.
( thread-and-action interface unit ) == uses ==> ( actions for TMyFormABCD unit ) <== uses == ( TMyFormABCD form declaration unit )
As a simplified option, you can declare those actions in the same units as forms themselves. Then you would have all form-units depend upon thread-unit, but thread-unit (remade to be generic and specific forms-agnostic) would no more depend upon any of forms-unit. Probably it might be called "Inversion of control".
See this series: http://www.uweraabe.de/Blog/2010/08/16/the-visitor-pattern-part-1/
And one more scheme to design this, which can be seen as implementing BOTH of those approaches - would be using Windows Messages.
Your "common interface", your "actions" would be represented by custom WM_xxx messages (integer consts) you would make. Then your thread would use PostMessage API to signal those actions to the forms. And those forms - by implementing methods to deal with those messages ( or by non-implementing = ignoring those messages ) would provide those action-implementations.
See: http://www.cryer.co.uk/brian/delphi/howto_send_custom_window_message.htm
PostMessage can be used from external thread but can not (easily) return values. SendMessage can only be used from the main Delphi thread. Also you have to check if MyTargetForm.HandleAllocated() before posting messages.

Putting classes in a DLL?

Is it possible to put some classes into a DLL?
I have several custom classes in a project I am working on and would like to have them put in a DLL and then accessed in the main application when needed, plus if they are in a DLL I can reuse these classes in other projects if I need to.
I found this link: http://www.delphipages.com/forum/showthread.php?t=84394 which discusses accessing classes in a DLL and it mentions delegating to a class-type property but I could not find any further information on this in the Delphi help or online.
Is there any reason I should not put classes in a DLL, and if it is ok is there a better way of doing it then in the example from the link above?
Thanks
It is not possible to get a Class/Instance from a DLL.
Instead of the class you can hand over an interface to the class.
Below you find a simple example
// The Interface-Deklaration for Main and DLL
unit StringFunctions_IntfU;
interface
type
IStringFunctions = interface
['{240B567B-E619-48E4-8CDA-F6A722F44A71}']
function CopyStr( const AStr : WideString; Index, Count : Integer ) : WideString;
end;
implementation
end.
The simple DLL
library StringFunctions;
uses
StringFunctions_IntfU; // use Interface-Deklaration
{$R *.res}
type
TStringFunctions = class( TInterfacedObject, IStringFunctions )
protected
function CopyStr( const AStr : WideString; Index : Integer; Count : Integer ) : WideString;
end;
{ TStringFunctions }
function TStringFunctions.CopyStr( const AStr : WideString; Index, Count : Integer ) : WideString;
begin
Result := Copy( AStr, Index, Count );
end;
function GetStringFunctions : IStringFunctions; stdcall; export;
begin
Result := TStringFunctions.Create;
end;
exports
GetStringFunctions;
begin
end.
And now the simple Main Program
uses
StringFunctions_IntfU; // use Interface-Deklaration
// Static link to external function
function GetStringFunctions : IStringFunctions; stdcall; external 'StringFunctions.dll' name 'GetStringFunctions';
procedure TMainView.Button1Click( Sender : TObject );
begin
Label1.Caption := GetStringFunctions.CopyStr( Edit1.Text, 1, 5 );
end;
Use runtime packages for this purpose; it's exactly what they're designed for in the first place. They get loaded automatically (or can be loaded manually), and automatically set up the sharing of the same memory manager so you can freely use classes and types between them.
You're much better off using packages (which is exactly what the IDE does for much of its functionality for that very reason).
Delphi does not support either importing or exporting classes from DLLs. To import a class from another module, you need to use packages.
While the official answer is "you can't", anything is possible of course. Frameworks like Remobjects SDK and Remobjects Hydra has been doing this for a long time. The problem is that it requires you to create an infrastructure around such a system, which is not something Delphi deals with out of the box.
The first step is memory management. A DLL is injected into the process loading it, but it does not share memory management. It has to be this way since a DLL can be created in a myriad of languages, each with their own internal mechanisms. This poses a problem with safety (i.e program writing into DLL memory and visa versa).
Secondly, interface (read: content description). How will your application know what classes it can create, class members, parameter types and so on. This is why COM requires type-libraries, which describe the content of a DLL.
Third, life-time management. If memory management for the objects created from a DLL is handled by the DLL, the DLL must also release said objects.
The above steps already exists and it's called COM. You are of course free to create as many COM DLL files as you please, just remember that these have to be registered with Windows before you use them. Either "on the fly" by your application (if you have the security rights to do so) or by your installer.
This is why COM, while representing the ultimate plugin system, is rarely used by Delphi programmers, because the technical cost of using it as a plugin system outweighs the benefits.
The alternative way
If you can assure that your DLL's are only to be used by Delphi programs, then you have a second way to explore. You have to create methods to share the memory manager of your main program with the DLL (Remobjects does this). This allows you to share objects, strings and more between the DLL and the main application.
You can then use RTTI to "map" classes stored in the DLL (the DLL must do this and generate both class and method tables) which can be invoked through a proxy class you device yourself.
All in all, unless you have plenty of free time to waste, I would either buy a system like Remobjects Hydra - or stick with packages. But can it be done another way? Of course it can. But at the cost of time and hard work.

In Delphi, Problem using Type definitions across units

Using Delphi 2010...
I have a set of binary properties I want to group together. I have defined it as such...
type
TTableAttributeType = (
tabROOT = 1,
tabONLINE = 2,
tabPARTITIONED = 3,
tabCOMPRESSED = 4,
);
// Make a set of of the Table Attribute types...
type
TTableAttrSet = Set of TTableAttributeType;
In my MAIN.PAS unit, I can create a variable of type TTableAttrSet.
Another Unit, UTILS.PAS needs to understand the TTableAttrSet type as well. That is taken care of by the USES clauses...
Main USES Util...
Util USES Main (The 2nd uses clauses, under implementation section, so I don't get circular reference problems....
So far so good. My problem is that I need to pass a var variable of type TTableAttrSet FROM main to Utils.
In main.pas
procedure TForm1.Button1Click(Sender: TObject);
var
TabAttr : TTableAttrSet;
begin
TestAttr (TabAttr);
end;
and in UTILS.PAS, I have
procedure TestAttr(var Attr: TTableAttrSet);
begin
Attr := [tabROOT, tabCOMPRESSED];
end;
When I try this, I run into several problems...
Problem 1). When I define my procedure definition at the top of utils.pas,
procedure TestAttr(var Attr: TTableAttrSet);
I get the error that TTableAttrSet is an Undeclared Identifier. This makes sense because the definition is in Main.pas, and the 'uses Main.pas' is AFTER my procedure definitions. How do I get around this? For now, I have duplicated the TTableAttrSet type definition at the top of the Utils.pas file as well as Main.pas, but this does not 'seem the right way'.
Problem 2). The bigger issue that I am running into is a compile error. on the calling line in Main.pas
TestAttr(TabAttr);
I get the error 'Types of actual and formal var parameters must be identifical'. To my knowledge they are identical. What is the compiler complaining about?
The simple solution is to move the declaration of TTableAttributeType to the Utils unit. You can't declare it twice because then you have two distinct types. That's no use to you, you need only a single type.
This solution will work so long as the Main unit does not need to reference TTableAttributeType in its interface section. Since the Utils unit clearly needs to do so then that would create a circular dependency between unit interface sections which is illegal.
If both the Main and Utils units need to reference TTableAttributeType in their interface sections then you need to create another unit that just contains type declarations. That unit could be used by both Utils and Main in their interface sections.

Resources