riktek
Member
- Local time
- Today, 11:40
- Joined
- Dec 15, 2023
- Messages
- 165
The Access.Application class has no events, unlike the Excel.Application class, so Access users are left to their own devices. The most critically important application behavior occurs on a project reset because this instantly and thoroughly disables an Access application, clearing all its variables and thereby destroying virtually all its runtime objects. The ability to trap this behavior and raise an event application-wide offers the opportunity for an application to become self-healing. Here’s how.
Among the runtime objects a project reset destroys are all event sources and sinks because both must be objects defined in class modules and such objects can exist only so long as pointers to them. The variables ultimately containing those pointers will survive a reset but the pointers will not. So, we must accommodate these difficulties, which will require a suitable runtime environment. It must of course be durable and lightweight, and also autostart and self heal.
We then create an application module named
We then create an application form,
In
Elsewhere, we create
Returning to
We then create
Returning to
We then create
In
In
Back again to
Also in
Finally, we return to
With this, the startup event progression is:
• Startup opens
•
•
•
•
•
•
The Reset event progression is:
• A project reset destroys the
•
•
•
•
•
•
•
•
•
• The
The shutdown event progression is:
•
•
So, with this, we have created:
• An application events framework that permits any behavior trap to trigger an application event.
• A reset trap.
• In a standard module:
• A self-healing application event source property that any form, report, or class can use to raise or sink application events.
• A public variable to contain the default application event sink.
• Ultimately, a durable, lightweight, self-healing, and autostarting runtime environment.
The code in toto is attached.
- Eric Blomquist
EDIT:
• Zipped modules now incorporate Debug.Print lines to demonstrate event progression.
• Zipped ACCDB incorporates zipped modules and is 32-bit A2007, so should run in all subsequent versions.
Among the runtime objects a project reset destroys are all event sources and sinks because both must be objects defined in class modules and such objects can exist only so long as pointers to them. The variables ultimately containing those pointers will survive a reset but the pointers will not. So, we must accommodate these difficulties, which will require a suitable runtime environment. It must of course be durable and lightweight, and also autostart and self heal.
Form_Unload() and Form_Close() will run after a project reset even if Class_Terminate() does not, so to trap a project reset, we create frmCanary and a Form_Unload() in its module.We then create an application module named
My to contain application properties and methods. My must be a standard module because it must be impervious to instancing so as to enforce a singleton pattern, and its procedures and variables must survive a reset.We then create an application form,
frmMain, which together with My constitute a durable application runtime environment. frmMain cannot be the application display (startup) form because it must open hidden, so we also create frmStartup and designate it as the application display form. The sole utility of any display form is its Form_Open() event procedure because it is the first event procedure that can run after application startup, and that event procedure itself must be cancelled because the form will become visible if Form_Load() is allowed to run. We thus must condition frmStartup.Form_Open()’s Cancel argument on frmMain loading successfully, as further described.In
My, we then create Main as an application property to return frmMain. We back Public Property Get Main() with a static local variable because it is identical to a private module-level variable in that it retains its value during the lifetime of its module. We also employ a self-healing object variable (SHOV) pattern, conditionally setting the variable on the procedure’s call if it is Nothing, to avoid the necessity of a Property Set procedure.Elsewhere, we create
AppIsLoaded(), a convenience function for readability to test My.Main().
Code:
Function AppIsLoaded() As Boolean
AppIsLoaded = (Not (My.Main Is Nothing))
End Function 'AppIsLoaded()
Returning to
frmStartup.Form_Open(), we therein do:
Code:
Cancel = AppIsLoaded
We then create
clsAppEventSource to define an application event source and therein declare an Access.Form variable named Canary, assign an instance of frmCanary to that variable in its Class_Initialize(), and destroy it in its Class_Terminate(). We also declare event Reset() and define a public OnReset() method to raise the Reset event.Returning to
My, we then create AppEventSource() as an application property, also implementing a SHOV pattern to avoid the necessity of a Property Set procedure.We then create
clsAppEventSink to define a default application event sink. Therein, we declare WithEvents a clsAppEventSource variable named oAppEventSource, assign My.AppEventSource() to it in Class_Initialize(), and clear that variable in Class_Terminate(). We go on to create oAppEventSource_Reset() to sink the event.In
frmMain, we create a public Init() and therein do:
Code:
Set My.AppEventSink = New clsAppEventSink
In
frmMain.Form_Open(), we also do:
Code:
Init
Back again to
My, we create OnReset() and therein do:
Code:
If gblnUnloading Then 'Case application closing.
' Do nothing. Exit gracefully without reloading.
Else 'Case project reset.
Main.Init
AppEvents.OnReset
End If
Also in
My, we create a public Boolean variable gblnUnloading and in frmMain.Form_Unload() do:
Code:
gblnUnloading = True
Finally, we return to
frmCanary.Form_Unload(), and therein do:
Code:
My.OnReset
With this, the startup event progression is:
• Startup opens
frmStartup.•
frmStartup.Form_Open() opens frmMain by testing My.Main().•
frmMain.Form_Open() calls frmMain.Init().•
frmMain.Init() initializes clsAppEventSink and assigns it to My.AppEventSink.•
clsAppEventSink.Class_Initialize() assigns My.AppEventSource() to clsAppEventSink.oAppEventSource.•
My.AppEventSource() initializes clsAppEventSource and returns an instance to clsAppEventSink.Class_Initialize().•
clsAppEventSource.Class_Initialize() initializes frmCanary and assigns it to clsAppEventSource.Canary.The Reset event progression is:
• A project reset destroys the
clsAppEventSource instance and its Canary variable.•
frmCanary.Form_Unload() calls My.OnReset(), which is unaffected by the reset because it is in a standard module.•
My.OnReset() calls frmMain.Init() via My.Main().•
My.Main() reinitializes its static form variable lfrmMain.•
frmMain.Init() reinitializes My.AppEventSink.•
clsAppEventSink.Class_Initialize() calls My.AppEventSource().•
My.AppEventSource() reinitializes clsAppEventSource.•
clsAppEventSource.Class_Initialize() reinitializes frmCanary.•
My.OnReset() calls My.AppEventSource.OnReset().•
clsAppEventSource.OnReset() raises clsAppEventSource.Reset().• The
clsAppEventSink instance assigned to My.AppEventSink sinks and handles clsAppEventSource.Reset().The shutdown event progression is:
•
frmMain.Form_Unload() is the first event to fire, so in it, we set My.gblnUnloading.•
My.OnReset() is triggered as objects collapse but skips reinitialization because gblnUnloading = True.So, with this, we have created:
• An application events framework that permits any behavior trap to trigger an application event.
• A reset trap.
• In a standard module:
• A self-healing application event source property that any form, report, or class can use to raise or sink application events.
• A public variable to contain the default application event sink.
• Ultimately, a durable, lightweight, self-healing, and autostarting runtime environment.
The code in toto is attached.
- Eric Blomquist
EDIT:
• Zipped modules now incorporate Debug.Print lines to demonstrate event progression.
• Zipped ACCDB incorporates zipped modules and is 32-bit A2007, so should run in all subsequent versions.
Attachments
Last edited: