I started out creating a class which would act in some ways as a container that includes an IPCSocket and several methods. Within the ‘container’ class’s constructor, I use AddHandler code:
WebSvrIPC_Socket = New IPCSocket
AddHandler WebSvrIPC_Socket.Error, AddressOf WebSvrIPC_Err
AddHandler WebSvrIPC_Socket.DataAvailable, AddressOf WebSvrIPC_DataAvailable
AddHandler WebSvrIPC_Socket.Connected , AddressOf Connected //WebSvrIPC_Connected
The properties whos AddressOf are all Private. Most all of them contain code that is used within the ‘container’ class. However, some of the IPCSocket classes events, I need to expose, at least in some degree, to parts of the main project. To try and make this work, I have included at the end of the event code to RaiseEvent [eventname]:
Private Sub WebSvrIPC_Err(IPC As IPCSocket)
IsConnected = False
LastError = WebSvrIPC_Socket.LastErrorCode
IsListening = False
If IsServer Then
IsListening = True
//Start timer to reconnect
//OpenDriverError = 100
//LostConnection = 102
//NameResolutionError = 103
//AddressInUseError = 105
//InvalidStateError = 106
//InvalidPortError = 107
//OutOfMemoryError = 108
Event Error(Err As Integer)
For some reason, the Event Definition I created, is not visible when I try to use AddHandler in the main project. However, public properties of the ‘container’ class are visible.
Not sure where I have gone wrong or what I am missing. I’ve reviewed the forum as well as the user guide(s) and other help tools. Unfortunately I have not found anything like this.
Any help/ideas or sample code would be really appreciated.
What do you mean by “not visible”? Does the event not show in auto-complete after AddHandler or does it not compile?
Not related to your question: why don’t you create an IPCSocket subclass instead of wrapping an IPCSocket into a class?
Eli beat me to it. If you mean that the event name doesn’t show up in auto-complete, this is expected. Just type it out “manually”.
However, don’t use AddressOf, use WeakAddressOf or the instance of your object will never go away and you’ll create a leak. Remember to use RemoveHandler to balance out the AddHandler calls too.
Hi guys - thanks for the pointed and interesting replies!
I mean by “Not visible” that yes, they are not in the auto-complete.
The code word “Error” in the line AddHandler “WebSvrIPC.Error” does not appear in auto complete.
AddHandler WebSvrIPC.Error, AddressOf WebSvrIPC_Err
Also, what I failed to mention is that the code fails to compile.
Second, I have tossed about subclassing IPCSocket Vs creating a new class that includes the IPCSocket as well as some others. Not really sure which method is the best. In both case I want code in the events, but not exposed to the main project. However, I need events to notify other parts of the project.
Third, Kem you brought up something interesting that I have not been aware of before now.
Should Weak be used only when the AddressOf is to be killed before the application is closed, or all of the time? And/Or should Weak be used when, for example that code is used within a custom class such as what I am doing?
Where should RemoveHandler be utilized?
Thank you both again!
I think I may have found my problem - or at least one of them.
In the main app, the Methods that are used in the AddressOf statements, did not have the correct code in then Parameters section. I used “IPC As IPCSocket” instead of “WebSvrIPC As clsWebSvrIPC”.
Other may be wrong too - and am especially curious about the use of WeakAddressOf and RemoveHandler issues brought up by Kem.
Thank you both again,
I duplicated your setup and while it did not autocomplete the error event, it did compile and run correctly. Make sure the method that you are AddHandler’ing to handle the error event has your containercontrol class as its first parameter.
Our replies crossed. In the container control, the methods used in AddHandler should have an IPCSocket for the first parameter. In the main app, the methods should have your containercontrol class as the first parameter. (I think that’s what you referred to in your reply - clsWebServIPC.)
Yes that was the issue.
I wish that a sample of this type of adding event code would be included in the samples and/or the docs. The docs only talk about subclassing a built in object like a timer. The AddHandler docs do not mention having anything in the methods used.
Thank you all again for your responses!
When you use AddHandler with AddressOf, you are creating a reference to that object the same as if you had assigned the object to a variable. Since you are doing it within the class itself, you are creating a circular reference. As such, the normal route of getting rid of an object will not work. Even after you’ve lost access to it by removing all other references, the object will remain since it references itself and its Destructor will never fire.
By using WeakAddressOf, you do not create a reference and the object will go away normally, similar to using WeakRef instead of assigning an object to a variable directly.
I do one of two things:
- Set up AddHandler with WeakAddressOf in the object’s Constructor, then add a corresponding RemoveHandler (again with WeakAddressOf) in the Destructor. This cleans things up properly.
- If the supporting object is set up in code, e.g., StartTimer, I make certain assumptions. For example, if the Timer exists, the handler has been added and must be removed. A corresponding StopTimer will do that, and I’ll call that from the Destructor too. You have to be careful with this approach though.
Is the above only done, or need to be done, if the object, IPCSocket for example, was subclassed? Or, because I created an instance of the IPCSocket within my own class, is the IPCSocket class destroyed naturally without literal code to do so?
However re-reading #2, seems to indicate that whenever an object is instantiated with code, it should be removed with code as well. Is this correct?
Thank you for the enlightenment!
The terminology got a bit confused, so let me do it this way:
I create a class,
MyClass, with a property,
MyClass.TheTimer As Timer.
MyClass.Constructor, I use the following code:
TheTimer = new Timer
AddHandler TheTimer.Action, AddressOf TheTimerActionHandler
TheTimer.Period = 1000
TheTimer.Mode = Timer.ModeMultiple
In the Destructor, I use:
If TheTimer isa object then
TheTimer.Mode = Timer.ModeOff
RemoveHandler TheTimer.Action, AddressOf TheTimerActionHandler
TheTimer = nil
Elsewhere, I do this:
dim var as new MyClass
// Do some stuff
var = nil
At this point, you’d expect
var.Destructor to fire, but it won’t. Why? Because using
AddHandler ... AddressOf ... has set up another reference to that instance, and this reference tells Xojo that you are still using the object even though you have no practical way of getting to it.
WeakAddressOf does not create this reference. Replace
WeakAddressOf in my sample code and it will all work.
Thank you for your time Kem.
Your code lays it out completely; particularly in the case of the Timer object.
This is all mandatory, even (or especially) when used in a Web Application inside an object within a Session.
Thank you again for sharing this. I had no idea even after all this time!