How to setup an Indy custom TIdIOHandler? - delphi

I want to use a custom TIdIOHandler for my Indy TCP client and I don't know how to set it up. I create a new IOHandler class, I registered with TIdIOHandler.RegisterIOHandler, and then I use TIdTCPClient.CreateIOHandler with my new handler reference. Now, when I try to write to the TIdTCPClient.Socket I get "Abstract error" exception... Do I need to configure other things besides my example ?
TIdEnhancedIOHandler = class(TIdIOHandlerSocket)
public
function Acknowledge(Command: Cardinal = 0): Boolean;
end
{this is a client in a thread}
constructor TMyTCPClient.Create(const AHost: String; APort: Word);
begin
inherited Create;
FreeOnTerminate:= True;
TIdEnhancedIOHandler.RegisterIOHandler;
TCPClient:= TIdTCPClient.Create;
TCPClient.CreateIOHandler(TIdEnhancedIOHandler);
TCPClient.ConnectTimeout:= 1000;
TCPClient.ReadTimeout:= -1;
TCPClient.Host:= AHost;
TCPClient.Port:= APort;
RetSuccess:= False;
RetMessage:= 'Unknown error.';
end;
procedure TMainClient.Execute;
var CMD: Cardinal;
begin
TCPClient.Connect;
TCPClient.Socket.Write(CMD);
end;

From the Indy Help I understand that ...
The help is old, and has outdated information.
I must use CreateIOHandler and specify my custom IOHandler class.
You can do that, but you don't actually need to. You can simply assign an instance of your custom class directly to the TIdTCPClient.IOHandler property, before calling TIdTCPClient.Connect(), eg:
TCPClient := TIdTCPClient.Create;
TCPClient.IOHandler :- TIdEnhancedIOHandler.Create(TCPClient);
...
I do that and I get an exception which says that this class is not installed.
That means Indy's internal GIOHandlerClassList does not contain any class types that derive from the class type you specify to CreateIOHandler().
How cand I install it ? The variable GIOHandlerClassList that holds the list is private...
You would need to call the public RegisterIOHandler() or SetDefaultClass() class method on your custom class type at runtime, such as in your unit's initialization section, eg:
initialization
TIdEnhancedIOHandler.RegisterIOHandler();
However, the only thing in Indy that actually uses CreateIOHandler() is TIdSimpleServer, so there is no need to "register" your custom class in this situation.
On the other hand, TIdTCPClient.Connect() does call TIdIOHandler.MakeDefaultIOHandler() if no IOHandler is assigned, and that uses GIOHandlerClassDefault, which is set by TIdIOHandler.SetDefaultClass() (which also calls RegisterIOHandler()). So, if you want TIdTCPClient to create an object of your custom class type for you, you would need to call SetDefaultClass() at least, eg:
initialization
TIdEnhancedIOHandler.SetDefaultClass();
Either way, RegisterIOHandler()/SetDefaultClass() should be called only one time, such as at program startup.
UPDATE: you are getting an abstract error because you are deriving your class from TIdIOHandlerSocket, which is an abstract class, as it does not implement the ReadDataFromSource() and WriteDataToTarget() methods that are declared as abstract in TIdIOHandler. You need to derive your class from TIdIOHandlerStack instead, which derives from TIdIOHandlerSocket and implements those methods.

Related

Delphi - Down casting object does not call base method

I have a base object type : TBServiceBookings and then I've derived another object type from that : TBServiceQuotes
So when my form is created I decide which object to use. In this instance I've created the derived object.
if fScreenType = ST_NewAppointment then
fBookingObject := TBServiceBookings.CreateServiceBookings(nil,botSingle)
else
fBookingObject := TBServiceQuotes.CreateServiceQuotes(nil,botSingle);
At some stage I want to call a method of the base class only. So I cast the derived object to the base type and call it's method, but it keeps on going to the derived method - which I don't want.
I've tried:
1) fBookingObject := TBServiceBookings(fBookingObject);
fBookingObject.SetupNewAppointmentScreen;
2)
TBServiceBookings(fBookingObject).SetupNewAppointmentScreen;
3)
(fBookingObject as TBServiceBookings).SetupNewAppointmentScreen;
What am I missing? Why does the derived method get called each time even though I've downcasted specifically to call the base method?
The only option I have left is to create a new variable of the base type and then carry on with that. But I already have a form variable which is my object, I just want to call a specific base class method.
Any help appreciated please!
Besides the question that lies behind your question "where do you need this unusual and somewhat suspicious construct for?", there are some possibilities to access an ancestor virtual method.
Really ugly: change the type of your component:
var
SaveType: TClass;
begin
SaveType := Self.ClassType;
PClass(Self)^ := TAncestor;
try
Self.AncestorMethod;
finally
PClass(Self)^ := SaveType
end;
end;
Cast the method instead of the class:
type
TInstanceMethod = procedure(Instance: TObject);
begin
TInstanceMethod(#TAncestor.AncestorMethod)(Self);
end;
Employ a class helper:
type
TAncestorHelper = class helper for TAncestor
procedure AncestorMethodViaHelper;
end;
procedure TAncestorHelper.AncestorMethodViaHelper;
begin
inherited AncestorMethod;
end;
begin
Self.AncestorMethodViaHelper;
end;
When in need, I myself always use the second solution. But that is only when dealing with ancestors I cannot change, e.g. the VCL. Within your own framework, you would never need these hacks because you can just redesign.
Well if the class is yours, you have full control, so just don't override the base method you want to call. Like :
fBaseObject.ThisMethodBase; { calls the original }
fDerivedObject.ThisMethod; { calls the new one }
Seems like the simplest way to do it. Also remember you can simply call the Inherited method from your overriden method. So if you want to get creative you can pass a boolean that indicates if you want the base functionality. Like :
type
TX1=class
function ThisMethod(whatever:Integer;callOldOne:Boolean=false):integer;virtual;
end;
TX2=class(TX1)
function ThisMethod(whatever:Integer;callOldOne:Boolean=false):integer;override;
end;
function TX1.ThisMethod(whatever:Integer;callOldOne:Boolean=false):integer;
begin
result:=1;
end;
function TX2.ThisMethod(whatever:Integer;callOldOne:Boolean=false):integer;
begin
if callOldOne then result:=inherited ThisMethod(whatever) else Result:=2;
end;
Object oriented programming is fun.

Delphi component property class depending on the component's Owner class

I'm using RAD Studio XE5 to build my application.
I saw that is was not very practical to try tu publish properties on a TForm. It then has to be registered and installed as a package, then it's not practical for heavy development.
So, I decided I would create a non visual component (TFormPropertiesEditor) that would be used to fill up form properties. A way of standardising my forms.
The component would be dropped on the base form, a form of which every other form inherits (let's call it TBaseForm). So, the component would be dropped only once on the 'base' form, then with inheritance, every other form would have it too.
The created component would detect the class of its Owner (BaseForm or its descendents) and create an object accessible through the 'Properties' property, whose class would be conditional to the owner class.
This way, when inspecting the component on a TBaseForm, I would have access to the TBaseFormProperties only. When inspecting the component on a TSecondForm, I would also have access to the TSecondFormProperties. Only, the component would be intelligent enough to detect which PropertyClass it should expose as the Properties property.
The component would inspect the form, through the GetPropertiesClass, defined as :
function TBaseForm.GetPropertiesClass : TPropertiesClass;
begin
Result := TBaseFormProperties;
end;
function TSecondForm.GetPropertiesClass : TPropertiesClass;
begin
Result := TSecondFormProperties;
end;
Each form has a corresponding TProperties descendent, like so :
TBaseForm ------------ TSecondForm ------------- ...
|
TBaseFormProperties -- TSecondFormProperties --- ...
For example :
If the Form on which the component is placed is TBaseForm, FProperties would be a TBaseFormProperties. If the form is a TSecondForm, FProperties would be TSecondFormProperties. Naturally, TSecondFormProperties would inherit from TBaseFormProperties.
Though, when I place the component on the form, it seems to be unable to detect of which class the component is.
function TFormPropertiesEditor.GetPropertiesClass: TFormPropertiesClass;
begin
Result := TBaseForm(Owner).GetPropertiesClass;
end;
It looks like the TBaseForm(Owner) part is causing the problem. The interpreter is stuck on the TBaseForm, and will not consider if Owner is of type TSecondForm or TThirdForm.
Interfaces
So, to get around the TBaseForm(Owner) typecasting, I decided to use an interface. So if I use an interface that declares the GetPropertiesClass:
IMasterForm = interface(IInterface)
['{B6122F34-65C4-4701-8A5E-50C8DABF5516}']
function GetPropertiesClass : TFormPropertiesClass;
end;
type
TBaseForm = class(TForm, IMasterForm)
MyFormPropertiesEditor1: TMyFormPropertiesEditor;
private
{ Déclarations privées }
public
function GetPropertiesClass : UCommon.TFormPropertiesClass;
end;
The following :
function TFormPropertiesEditor.GetPropertiesClass : TFormPropertiesClass;
begin
Result := (Owner as IMasterForm).GetPropertiesClass;
end;
Results into an Interface not supported error.
Abstract Ancestor Method
Then, I decided to add an extra layer of ancestry. I've added a class, TMasterForm, from which TBaseForm inherits. This TMasterForm declares GetPropertiesClass as abstract and virtual :
TMasterForm = class(TForm, IMasterForm)
public
function GetPropertiesClass : TFormPropertiesClass; virtual; abstract;
end;
type
TBaseForm = class(TMasterForm)
private
{ Déclarations privées }
public
function GetPropertiesClass : UCommon.TFormPropertiesClass; override;
end;
But then, I get an AV because I think the IDE tries to access TMasterClass.GetPropertiesClass, which is of course not implemented.
How can this TypeCasting be accomplished? Any idea how could I proceed ?
Thank you very much in advance
Download Sample Project https://www.wetransfer.com/downloads/b524438609fc04257af803a8e3dd2eca20141225161239/764d108d335b9d296c3004dfea04a54620141225161240/9c8cc0
The basic problem here is that the IDE does not instantiate your form at designtime. So, no matter what code you put in the form class, it won't be executed by the IDE. This is because you did not register the form with the IDE.
If you wish the IDE to have knowledge of your forms then you need to register them with the IDE. And at that point all your code becomes needless because you are back to doing what you are attempting to avoid. Namely registering the forms with the IDE. You are stuck in a Catch 22 situation. If you need the IDE to know about the forms then you need to register them. At which point you may as well just surface the properties directly in the Object Inspector.
The problem in your code is that you are not inheriting your GetPropertiesClass method properly.
Infact you are not inheriting it across the class family.
In your code each class type has its own version of GetPropertiesClass method and therefore since you are typecasting the Owner to a TBaseForm class the method from TBaseForm is being used even if Owner is of TSecondForm class.
So you need to make sure that GetPropertiesClass in TBaseForm class is virtual and that merhod GetPropertiesClass in TSecondForm is overrided.
This will ensure that TSecondForm.GetProperties method will be called even when you are typecasting Owner to TBaseClass when the owner is of TSeconfForm class.

How to check if a method was implemented in class Delphi

I need to check if method was implemented in class.
My classes implements an interface.
Code.
IMyInterface = interface
['{538C19EB-22E3-478D-B163-741D6BB29991}']
procedure Show();
end;
TMyClassFormCustomer = class(TInterfacedObject, IMyInterface)
public
procedure Show();
end;
TMyClassFormVendors = class(TInterfacedObject, IMyInterface)
public
procedure Show();
end;
....
procedure TMyClassFormCustomer.Show;
var
Form: TFormCustomer;
begin
Form:= TFormCustomer.Create(nil);
try
Form.ShowModal();
finally
FreeAndNil(Form)
end;
end;
{ TMyClassFormVendors }
procedure TMyClassFormVendors.Show;
begin
end;
It is possible to check if method TMyClassFormVendors.Show have implementation?
When a method not have implementation I have disable item in menu
Thanks.
I see a couple of possibilities:
If your TMyClassFormVendors does not want to do anything in .Show then simply don't declare and implement IMyInterface. In your code you can then query for that interface with if Supports(MyClass, IMyInterface) and react accordingly (Disable Menu entry)
If your IMyInterface interface actually has more than one method declared, some of which are supported (I don't want to use the word implemented, as all methods have to be implemented) and others are not, then you should better split the interface into several different ones and proceed as described in 1
You could also declare and implement another interface "ICapabilities" that could have methods like CanShow etc. In your code you could then query your class like if (Myclass as ICapabilities).CanShow then ...
I personally would favour 2. as it is the cleanest approach IMO, but it depends on what you want to do specifically
You could use similar approach as it is used for handling the event methods. So what you want to do is instead of implementing the method directly into your class you add a property which can store a method pointer to the actual method you wanna use. And then at runtime you only check to see if the pointer to such method was already assigned or not.
The code for this would look something like this:
type
//Method type object
TMyMethod = procedure (SomeParameter: Integer) of object;
TMyClass = class(TObject)
private
//Field storing pointer to the method object
FMyMethod: TMyMethod;
protected
...
public
//proerty used for assigning and accesing the method
property MyMethod: TMyMethod read FMyMethod write FMyMethod;
end;
When you are designing the method objects you can define how many and which type of parameters you wanna use in that method.
Don't forget you need to assign method to your method property in order to use it. You can do this at any time. You can even unassign it by setting it to nil if needed.
You do this in same way as you do with event methods
MyClass.MyMethod := MyProcedure;
where MyProcedure needs to have the same parameter structure as the method object type you defined earlier.
And finally you can check to see if methods has been assigned at runtime using:
if Assigned(MyClass.MyMethod) then
begin
//Do something
end
else //Skip forward
EDIT: Another advantage of this approach is that your actual method that you use can be defined and written in completely different unit.

How to make a Web Module know the owner from where it was called?

I'm working on an HTTP Server and I'm using Indy's TIdHTTPWebBrokerBridge with a TWebModule. I'm encapsulating all the Server functionality into a single Component, and this includes the Indy Server component and its corresponding Web Module. However, I'm having some issues figuring out how to make the Web Module know the Component from which it was called.
Suppose I have this component:
type
TMyComponent = class(TComponent)
private
FServer: TIdHTTPWebBrokerBridge;
end;
implementation
uses
MyWebModule;
I do know I have to initialize this by setting the Request Handler, and I've taken care of it by adding an initialization section to this unit:
initialization
if WebRequestHandler <> nil then
WebRequestHandler.WebModuleClass:= WebModuleClass;
Then I have the Web Module class in a separate unit:
uses
MyWebServer;
type
TMyWebModule = class(TWebModule)
private
FOwner: TMyComponent;
end;
Note the private field I have in this web module FOwner: TMyComponent. This is where I'm having trouble figuring out where to go. How do I assign this to its appropriate owner? The Web Module is automatically created and managed by the Indy HTTP Server, so as far as I know, I don't have any control to set such a thing.
The reason why I need to have access to its owner is because I have properties set there which the Web Module needs to be able to read. For example, one property I have on my component is RootDir which is a root directory where to read/write files. I need to be able to read this property from within the Web Module.
How can I make the Web Module able to read its owner component's properties? Or in general, how can I assign this private field FOwner to the instance of my component from where it was instantiated?
I discovered my answer a while after asking this question
Since it's not advised to create multiple instances of TIdHTTPWebBrokerBridge, you don't necessarily need to worry about the existence of multiple different instances of TMyComponent. You do however need to perform your own checking to make sure multiple instances don't exist in the first place. But since you only have one instance of this Component, you can feel safe declaring a global variable in your component's unit and exposing it to your web module.
Don't just declare a global var variable directly in the interface of your unit. Instead, you should protect this...
function MyComponent: TMyComponent;
implementation
uses
MyWebModule;
var
_MyComponent: TMyComponent;
function MyComponent: TMyComponent;
begin
Result:= _MyComponent;
end;
constructor TMyComponent.Create(AOwner: TComponent);
begin
inherited;
_MyComponent:= Self;
end;
destructor TMyComponent.Destroy;
begin
_MyComponent:= nil;
inherited;
end;
initialization
_MyComponent:= nil;
if WebRequestHandler <> nil then
WebRequestHandler.WebModuleClass:= WebModuleClass;
end.

Delphi Web Script: How to Expose a Class via RTTI which contains a Method returning another (exposed) Class

I have this Delphi class
type
TAnotherClass = class
end;
TMyClass = class
function Foo: TAnotherClass;
end;
function TMyClass.Foo: TAnotherClass;
begin
Result := TAnotherClass.Create;
end;
Now I'd like to expose this class via "dwsRTTIExposer.pas":
myUnit.ExposeRTTI(TypeInfo(TMyClass));
myUnit.ExposeRTTI(TypeInfo(TAnotherClass));
My Script looks like that:
var a: TMyClass = TMyClass.Create;
var b: TAnotherClass;
b := a.Foo;
Unfortunatelly Delphi Web Script doesn't recognize the return value from TMyClass.Foo as a valid Script Class. Is there a possibility to do that without falling back to manually expose each method with an OnEval-Eventhandler?
ExposeRTTI currently doesn't support parameters of class type.
This is because returning a direct Delphi class in the script can be problematic, as the life-cycle of Delphi objects is arbitrary and undetermined (the Delphi-side object can be destroyed at any time without notice f.i.).
You don't have to expose each method manually, you can use the RTTI exposer for every methods that involves basic types, and only have to deal manually with methods involving class types.
That'll then leave you with having to decide how you want the script-side objects to be exposed, and what their relationship to the Delphi-side object is, which is something the RTTI provides no clues about.
For instance with your original code, the OnEval code would just create a new script object that wraps the method Result for each call.
But the RTTI signature of Foo would still be exactly the same if its implementation was changed to something like
TMyClass = class
private
FFoo: TAnotherClass;
public
function Foo: TAnotherClass;
end;
function TMyClass.Foo: TAnotherClass;
begin
if FFoo=nil then
FFoo := TAnotherClass.Create;
Result := FFoo;
end;
However in that case, the OnEval would have to be completely different, as you would have to return the same script-side object on subsequent calls, and you would also need to hook the script-side object's destructor to properly handle the consequences on the private FFoo field.
Once Delphi has truly garbage-collected objects, the constraint could be relaxed, but currently the only thing that gets close is TInterfacedObject, which is unsafe, and you still have to deal with manual event handlers to handles things like circular references or classes that disable the reference counting (like the VCL components).

Resources