The Garbage collector in VBA (1 Viewer)

jwcolby54

Active member
Local time
Yesterday, 20:49
Joined
May 19, 2025
Messages
304
I had a poster in one of my threads post that if I did some thing his way it would avoid a circular reference, and how circular references could cause memory leaks (true) and how if Access crashed the end of the (access) world could commence. Not so true but worth dealing with. The point though is that "circular references" are not the problem specifically, rather the problem is not cleaning up our references to things when done with them, in this specific case to a form. So... garbage collection in VBA, what is it and how does it work?

I hit up ChatGPT with the following so that we could have a starting point for a discussion on garbage collection in VBA. It is a subject often not well understood. We all understand the pitfalls of AI and ChatGPT, just know that is where the following information came from.

My question to ChatGPT:

I need an in depth discussion about the vba garbage collector. How it works and what triggers it. I am teaching classes , events etc at an in depth level and the garbage collector needs to be understood. I will be starting a thread on Access-Programmers to explain the subject. What you provide here will be the starting point for what will likely be a lively discussion. So clarity is key.

ChatGPT's response:

Great — let’s dive deep into how memory management and object destruction work in VBA (Visual Basic for Applications), commonly referred to as the VBA "garbage collector" even though it doesn’t quite work like a true garbage collector (e.g., in .NET or Java). This discussion will help your students and readers understand how and when objects are cleaned up in VBA, what pitfalls to avoid, and how circular references and event handlers can complicate things.




🔍 What Is VBA’s "Garbage Collector"?​


VBA does not use a traditional garbage collector. Instead, it uses a reference counting system to manage memory for objects.

✔ Core Mechanism:​


Each VBA object has an internal reference count — a tally of how many variables (or other objects) are currently referring to it.
  • When a variable is set to refer to an object (Set obj = New SomeClass), the reference count increases by 1.
  • When that variable is set to Nothing or goes out of scope, the reference count decreases by 1.
  • When the reference count drops to 0, VBA immediately destroys the object and releases the associated memory.

This is deterministic destruction — you know exactly when the object will be destroyed.


🧠 What Triggers Object Destruction?​

Object destruction is triggered when:
  1. All references to an object are cleared or go out of scope

    Code:
    Sub Demo()    Dim obj As New MyClass
        ' obj has reference count = 1
    End Sub
    ' obj goes out of scope, count drops to 0 => destroyed
  2. You explicitly set the reference to Nothing
    Code:
    Set obj = Nothing  ' count drops to 0 => destroyed

  3. You overwrite a reference
    Code:
    Set obj = New MyClass  ' old object: count drops to 0, destroyed

🔁 Circular References: The Achilles Heel​

Reference counting fails when two objects reference each other — a circular reference — because each keeps the other alive.

Example:​

Code:
' ClassA has a reference to ClassB, and vice versa

'--- ClassA
Public Partner As ClassB

Private Sub Class_Terminate()
    Debug.Print "ClassA terminated"
End Sub

'--- ClassB
Public Partner As ClassA

Private Sub Class_Terminate()
    Debug.Print "ClassB terminated"
End Sub

Sub CreateLeak()
    Dim a As New ClassA
    Dim b As New ClassB
    Set a.Partner = b
    Set b.Partner = a

    ' Break external refs:
    Set a = Nothing
    Set b = Nothing
    ' Reference counts are still > 0 => memory leak
End Sub

🛠️ Fix: Manual breaking of the circular reference​

Before setting your objects to Nothing, you should clear the internal references explicitly:
Code:
Set a.Partner = Nothing
Set b.Partner = Nothing
Set a = Nothing
Set b = Nothing

🔥 VBA provides a pseudo-destructor in class modules:​

Code:
Private Sub Class_Terminate()
    ' Cleanup code here
End Sub
  • This fires automatically when the reference count hits 0.
  • You cannot call Class_Terminate directly.
  • It’s the place to:
    • Close files
    • Release resources
    • Break circular references (if needed)
    • Unhook event sinks

⚠️ Events and Memory Leaks​

Events are delegates — they cause the class that listens for events to hold a reference to the event source.

Common leak scenario:​

Code:
' Form1 creates ClassA and hooks to its events
Dim WithEvents obj As ClassA

Private Sub Form_Load()
    Set obj = New ClassA
End Sub

Form1 has a reference to obj, but now obj raises events back to Form1, which means obj has a hidden reference to Form1. Even if you Set obj = Nothing, the circular reference persists.

Fix: Unhook event sinks manually​

Code:
Set obj = Nothing  ' leaks Form1

Add a method on ClassA to null the event source:
Code:
Public Sub Unhook()
    Set EventTarget = Nothing
End Sub

Call it before destruction.

🧹 VBA's "Garbage Collector" in Summary​

ConceptDescription
TypeReference-counted memory management
Destruction TimingImmediate, deterministic (when ref count = 0)
DestructorClass_Terminate
Leaks?Yes, due to circular references or poorly handled events
Explicit Cleanup?Required when circular references or event sinks exist

👀 Teaching Tips​

  • Emphasize reference tracing. Draw diagrams showing object dependencies.
  • Reinforce that Set x = Nothing is about breaking links — it’s not “destroying” anything on its own.
  • Explain how static variables can also keep objects alive longer than intended.
  • Discuss Debug.Print or log output in Class_Terminate to track lifecycle.

💬 Bonus: Detecting Leaks​

You can track object lifecycle using a counter and Debug.Print:

Code:
Private Sub Class_Initialize()
    Debug.Print "Object created"
End Sub

Private Sub Class_Terminate()
    Debug.Print "Object destroyed"
End Sub

Or maintain a global counter:
Code:
Public gLiveCount As Long

Private Sub Class_Initialize()
    gLiveCount = gLiveCount + 1
End Sub

Private Sub Class_Terminate()
    gLiveCount = gLiveCount - 1
End Sub

OK. I am done presenting what ChatGPT wrote.

I hope this is useful to some of you. And I make no representation that it is useful or even correct. It is just information in the end.
 
Last edited:
That answer is correct as far as it goes, but there is an ASPECT of garbage collection not mentioned as such... memory re-usage.

All variables declared in Event Routines (except those declared as STATIC) are kept in something called the STACK FRAME. When an event routine becomes active (because its event occurred), the implied call creates a formal stack frame structure (that we don't easily get to see in pure Access code 'cause we can't reference the stack directly). In this frame structure, all local non-static variables are stored relative to the STACK POINTER (a hardware register that points to the current top of the program stack). When you exit from that event routine, its stack frame becomes out-of-scope ... forever when the stack pointer adjusts back to the previous frame. If the event occurs again, you get a freshly created frame. What makes this a facet of garbage collection is that the memory "above" the (new) top of the stack gets re-used with each call and return. That is, stack memory IS reclaimed and re-used in the current session.

When you dynamically create an object of any kind and later set its object-variable to Nothing, it is true that the memory associated with the (former) object is released. That is, the object's allocated memory is now out-of-scope for all purposes. However, the object was created in the HEAP, which differs from the STACK in usage pattern.

The stack is ALWAYS related to the position indicated by the hardware stack pointer - more precisely, the user stack, which is a per-process memory area. Typically, the HEAP is near to the same memory area (generally speaking) as the STACK. In terms of virtual addresses for programs developed in the standard manner (and I think this applies to Access), the STACK is at the top of the virtual memory range, which is address 0x7FFFFFC (for 32-bit Access front-end file.) The HEAP is allocated linearly starting from the next even memory page boundary above the highest actual program component specified in the .EXE file - which means that the size of the heap for Access depends on the number of libraries and other special things you include or reference at app startup.

Objects can be randomly released from the HEAP, unlike the stack which is far more deterministic in usage pattern. You CANNOT randomly release memory from the stack. Access does not (or at least, last time I heard, it didn't) re-use released HEAP memory. The stack, as previously described, CAN/DOES re-use memory. The HEAP's problem is memory fragmentation caused by variable-length structures being allocated from the HEAP area and released at random intervals.

As I understand it from a discussion a long time ago, the problem with the objects we create is that when we release them, if other objects have been created in the interim but not released yet, it might not be possible to fully rejoin that memory to other memory chunks. That occurs because object mapping is immutable. I.e. WHILE THE OBJECT STILL EXISTS, you can't move the object and merge it with other released memory. It sits there where it was originally allocated like a roadblock in merging released memory. Those of you who remember using disk defragmenters will understand that this is an analogous problem.

This becomes an issue as the HEAP gets more and more fragmented, forcing allocation from the yet untouched area of HEAP, until you get the dreaded error "HEAP overflows STACK" - although it could happen that you get its partner, "STACK overflows HEAP". The latter situation has occurred for me in my genealogy application trying to do some recursion in a somewhat less than ideal family tree where two people who were distant cousins in different generation levels got married. Talk about gumming up the works!!!

As a side note, there is a WINDOWS structure called a LookAside list that DOES re-use memory and WOULD make it possible to re-use at least some of HEAP memory. But I have not seen notes on its use in Access. If anyone finds that HEAP allocation will move to start using LookAside lists, it would be great news for program stability.

The forms you create and activate - particularly if they involve background images in the form's detail or other sections, is that the memory for that image ALSO comes from the heap. Our member Tom vanStiphout described this problem on another forum. This is significant in that typically you have a few megabytes in the stack/heap area, but if you have a really complex image embedded in your form, you just chewed up a LOT of space you won't get back. (Tom's suggested using lower-resolution graphics for the image as a way to reclaim memory.)

The GOOD news, however, is that once Access exits, THAT memory is reclaimed for re-use by Windows. You can see it if you use Windows Task Manager and call up the memory performance frame. There is the moving memory chart that shows memory usage for the last sixty seconds. Under chart that is a bar with four sections: In-Use memory, Modified memory, Standby memory, and Free memory. An exiting process's memory goes into the Standby memory if it was potentially shareable and into Free memory if not. That IS an example of garbage collection at its best.
 
That answer is correct as far as it goes, but there is an ASPECT of garbage collection not mentioned as such... memory re-usage.

All variables declared in Event Routines (except those declared as STATIC) are kept in something called the STACK FRAME. When an event routine becomes active (because its event occurred), the implied call creates a formal stack frame structure (that we don't easily get to see in pure Access code 'cause we can't reference the stack directly). In this frame structure, all local non-static variables are stored relative to the STACK POINTER (a hardware register that points to the current top of the program stack). When you exit from that event routine, its stack frame becomes out-of-scope ... forever when the stack pointer adjusts back to the previous frame. If the event occurs again, you get a freshly created frame. What makes this a facet of garbage collection is that the memory "above" the (new) top of the stack gets re-used with each call and return. That is, stack memory IS reclaimed and re-used in the current session.

When you dynamically create an object of any kind and later set its object-variable to Nothing, it is true that the memory associated with the (former) object is released. That is, the object's allocated memory is now out-of-scope for all purposes. However, the object was created in the HEAP, which differs from the STACK in usage pattern.

The stack is ALWAYS related to the position indicated by the hardware stack pointer - more precisely, the user stack, which is a per-process memory area. Typically, the HEAP is near to the same memory area (generally speaking) as the STACK. In terms of virtual addresses for programs developed in the standard manner (and I think this applies to Access), the STACK is at the top of the virtual memory range, which is address 0x7FFFFFC (for 32-bit Access front-end file.) The HEAP is allocated linearly starting from the next even memory page boundary above the highest actual program component specified in the .EXE file - which means that the size of the heap for Access depends on the number of libraries and other special things you include or reference at app startup.

Objects can be randomly released from the HEAP, unlike the stack which is far more deterministic in usage pattern. You CANNOT randomly release memory from the stack. Access does not (or at least, last time I heard, it didn't) re-use released HEAP memory. The stack, as previously described, CAN/DOES re-use memory. The HEAP's problem is memory fragmentation caused by variable-length structures being allocated from the HEAP area and released at random intervals.

As I understand it from a discussion a long time ago, the problem with the objects we create is that when we release them, if other objects have been created in the interim but not released yet, it might not be possible to fully rejoin that memory to other memory chunks. That occurs because object mapping is immutable. I.e. WHILE THE OBJECT STILL EXISTS, you can't move the object and merge it with other released memory. It sits there where it was originally allocated like a roadblock in merging released memory. Those of you who remember using disk defragmenters will understand that this is an analogous problem.

This becomes an issue as the HEAP gets more and more fragmented, forcing allocation from the yet untouched area of HEAP, until you get the dreaded error "HEAP overflows STACK" - although it could happen that you get its partner, "STACK overflows HEAP". The latter situation has occurred for me in my genealogy application trying to do some recursion in a somewhat less than ideal family tree where two people who were distant cousins in different generation levels got married. Talk about gumming up the works!!!

As a side note, there is a WINDOWS structure called a LookAside list that DOES re-use memory and WOULD make it possible to re-use at least some of HEAP memory. But I have not seen notes on its use in Access. If anyone finds that HEAP allocation will move to start using LookAside lists, it would be great news for program stability.

The forms you create and activate - particularly if they involve background images in the form's detail or other sections, is that the memory for that image ALSO comes from the heap. Our member Tom vanStiphout described this problem on another forum. This is significant in that typically you have a few megabytes in the stack/heap area, but if you have a really complex image embedded in your form, you just chewed up a LOT of space you won't get back. (Tom's suggested using lower-resolution graphics for the image as a way to reclaim memory.)

The GOOD news, however, is that once Access exits, THAT memory is reclaimed for re-use by Windows. You can see it if you use Windows Task Manager and call up the memory performance frame. There is the moving memory chart that shows memory usage for the last sixty seconds. Under chart that is a bar with four sections: In-Use memory, Modified memory, Standby memory, and Free memory. An exiting process's memory goes into the Standby memory if it was potentially shareable and into Free memory if not. That IS an example of garbage collection at its best.
@The_Doc_Man ... Even my eyes are crossing. You (and I to a lesser extent) are steeped in the actual hardware behind the scenes. I understand stacks and stack pointers, and heaps, FIFO, LIFO and all that jazz. That is all pretty esoteric to today's developers. Or at least Access developers. I grew up doing assembler, and understanding the registers inside of the processor, and the machine instructions, and how it moved stuff out of memory into a register, and back out again etc etc....

I was trained by the Navy to fix old Sperry Univac stuff, truly archaic stuff. With odd instruction widths, and 32 kilo-words (IIRC) of core memory. Big refrigerator sized monstrosities, plus the reel to reel, the IBM hard disks with changeable disk packs. Card readers, tty machines, plotters, and so forth. If it was in the computer center, I fixed it.

The first micro processor I was introduced to was the Intel 4004. Yea! a tiny little thing that... ran the weather station on the aircraft carrier in 1974 when I got to the Kennedy. I would not want to have the job of programming that thing.

My first computer that I owned, I built myself (in 1977) from boards purchased from advertisements in the back of Popular Electronics. It was a Zilog z-80 with 24 kbytes of static ram. Ooooohhh such power (in 1978). It loaded "zapple basic" off of a cassette deck. And I started learning to program Basic. I moved on to a home built 8086 with 256 kbytes of dynamic ram, a serial port to run a terminal and a dual 8" floppy. And oh yea, CPM86. On that I moved up to Borland's Turbo Pascal, and Turbo C.

Eventually it became too much even for me. Processors had hundreds or thousands of instructions, were micro-coded to break the CISC down to RISC pieces. Branch prediction. All kinds of things I simply didn't have the time, energy or desire to keep up with.

In any event (back to the subject at hand) Memory management is not something that I can really control at a useful level. Whether the object being released leaves a hole in memory too small to be used by another object... etc. Thankfully Microsoft hired engineers way more educated than I (you perhaps?) to write the code to handle this stuff. Windows is after all the ultimate framework. And Access is a framework on top of windows. A subject that I am trying to teach here. I am a little surprised that VBA would handle its own memory management to that level. I always just assumed it asked Windows for a chunk of memory, and handed it back to Windows when it was done. Definitely not my area of expertise.

And so for me, just understanding the "last reference to an object" is sufficient for my needs.

Good write-up though. You are discussing things I haven't thought about in decades.
 
Last edited:
That answer is correct as far as it goes, but there is an ASPECT of garbage collection not mentioned as such... memory re-usage.

All variables declared in Event Routines (except those declared as STATIC) are kept in something called the STACK FRAME. When an event routine becomes active (because its event occurred), the implied call creates a formal stack frame structure (that we don't easily get to see in pure Access code 'cause we can't reference the stack directly). In this frame structure, all local non-static variables are stored relative to the STACK POINTER (a hardware register that points to the current top of the program stack). When you exit from that event routine, its stack frame becomes out-of-scope ... forever when the stack pointer adjusts back to the previous frame. If the event occurs again, you get a freshly created frame. What makes this a facet of garbage collection is that the memory "above" the (new) top of the stack gets re-used with each call and return. That is, stack memory IS reclaimed and re-used in the current session.

When you dynamically create an object of any kind and later set its object-variable to Nothing, it is true that the memory associated with the (former) object is released. That is, the object's allocated memory is now out-of-scope for all purposes. However, the object was created in the HEAP, which differs from the STACK in usage pattern.

The stack is ALWAYS related to the position indicated by the hardware stack pointer - more precisely, the user stack, which is a per-process memory area. Typically, the HEAP is near to the same memory area (generally speaking) as the STACK. In terms of virtual addresses for programs developed in the standard manner (and I think this applies to Access), the STACK is at the top of the virtual memory range, which is address 0x7FFFFFC (for 32-bit Access front-end file.) The HEAP is allocated linearly starting from the next even memory page boundary above the highest actual program component specified in the .EXE file - which means that the size of the heap for Access depends on the number of libraries and other special things you include or reference at app startup.

Objects can be randomly released from the HEAP, unlike the stack which is far more deterministic in usage pattern. You CANNOT randomly release memory from the stack. Access does not (or at least, last time I heard, it didn't) re-use released HEAP memory. The stack, as previously described, CAN/DOES re-use memory. The HEAP's problem is memory fragmentation caused by variable-length structures being allocated from the HEAP area and released at random intervals.

As I understand it from a discussion a long time ago, the problem with the objects we create is that when we release them, if other objects have been created in the interim but not released yet, it might not be possible to fully rejoin that memory to other memory chunks. That occurs because object mapping is immutable. I.e. WHILE THE OBJECT STILL EXISTS, you can't move the object and merge it with other released memory. It sits there where it was originally allocated like a roadblock in merging released memory. Those of you who remember using disk defragmenters will understand that this is an analogous problem.

This becomes an issue as the HEAP gets more and more fragmented, forcing allocation from the yet untouched area of HEAP, until you get the dreaded error "HEAP overflows STACK" - although it could happen that you get its partner, "STACK overflows HEAP". The latter situation has occurred for me in my genealogy application trying to do some recursion in a somewhat less than ideal family tree where two people who were distant cousins in different generation levels got married. Talk about gumming up the works!!!

As a side note, there is a WINDOWS structure called a LookAside list that DOES re-use memory and WOULD make it possible to re-use at least some of HEAP memory. But I have not seen notes on its use in Access. If anyone finds that HEAP allocation will move to start using LookAside lists, it would be great news for program stability.

The forms you create and activate - particularly if they involve background images in the form's detail or other sections, is that the memory for that image ALSO comes from the heap. Our member Tom vanStiphout described this problem on another forum. This is significant in that typically you have a few megabytes in the stack/heap area, but if you have a really complex image embedded in your form, you just chewed up a LOT of space you won't get back. (Tom's suggested using lower-resolution graphics for the image as a way to reclaim memory.)

The GOOD news, however, is that once Access exits, THAT memory is reclaimed for re-use by Windows. You can see it if you use Windows Task Manager and call up the memory performance frame. There is the moving memory chart that shows memory usage for the last sixty seconds. Under chart that is a bar with four sections: In-Use memory, Modified memory, Standby memory, and Free memory. An exiting process's memory goes into the Standby memory if it was potentially shareable and into Free memory if not. That IS an example of garbage collection at its best.
Do us a favor and at the very bottom put a little "actionable items" piece. What if anything can we do to reduce the problems you experiences.
 
The best way to minimize "heap thrashing" is to create your major objects and don't dissolve them - re-use them when possible. Creating and destroying application objects "churns" memory.
 
The best way to minimize "heap thrashing" is to create your major objects and don't dissolve them - re-use them when possible. Creating and destroying application objects "churns" memory.
Hmmm, trying to figure out how I could do something like this. For example an app using my framework loads a form. That kicks off an entire process. clsFrm loads, the control scanner runs, finding all of the controls that my framework wants to deal with - combo boxes, text boxes, tab controls, subform controls on tab pages etc. If the form is at all complex there will be dozens, or even hundreds of class instances loaded, wrapping each of those access objects. They get torn down in reverse order as the form closes. It feels like the "only" thing I could do is to set a form invisible instead of closing it.

I did JIT subforms on tab controls, loading the subforms on a given tab page only when that tab page is loaded. I was unloading the subform control(s) as the tab is clicked off of. The issue here is that when the main form changes to a new record, the subforms tend to requery to show children of that parent form. So unloading the subforms not actively being displayed sped up the "feel" of the form as the user moved from parent record to parent record.

I didn't do much with images however. I do know that having pointers to the image, with the image stored out on disk was considered "better" than actually trying to embed them on the form. I have to say though that I never got either of the errors you are discussing about heap / stack collisions.
 
The first micro processor I was introduced to was the Intel 4004.

For me, a PDP-11/15 in the "Lab" configuration. It came on a standard-width rack, on wheels, and had several connection points that we could use to connect to our experiments. For me, it performed real-time data acquisition via a photomultiplier with a diffraction grating that allowed us to shine monochromatic light through a mixing cuvette to do chemical reaction-rate studies on the formation of a particular chromophore (i.e. something that changed colors due to the reaction). Paper-tape O/S, but we had a cross-assembler on the PDP-10 that could build paper tape self-loading programs. Did my dissertation on that thing. We got data rates of up to 5000 samples per second, got repeatable experiments, and got our results back (reaction rate values) in 2-5 seconds depending on reaction speed.

And oh yea, CPM86. On that I moved up to Borland's Turbo Pascal

I finally got around to that on my Osborn-1, which was a Z80 running CP/M-80. I used Turbo Pascal to simulate the orbits of four moons for some of my novels where in a sword-and-sorcery world, the phases of the moons were "signs and portents" of some significance. Amazing what you can do with a few simple formulas and a decent compiler.

Thankfully Microsoft hired engineers way more educated than I (you perhaps?) to write the code to handle this stuff.

His name was Dave Cutler, listed as one of the primary authors of WinNT. Prior to that version, ALL Windows platforms were layers over MS-DOS. WinNT was the first version of Windows where the DOS prompt was the layer and Windows was the base. There was a time when Microsoft and Digital Equipment Corp. had a teaming agreement. During that time, they stole learned the paging / swapping algorithm from VAX/VMS and also got the stack-frame concepts that are used in Access today. Also, VAX implemented the trick of setting memory to enforce things like "execute-only code" or "user-read, kernel write" or various other ideas about marking instructions and data differently to prevent inappropriate use. For Windows, that is Data Execution Prevention and there is a corresponding feature to prevent instructions from being modified.

I think Dave is still around and kicking. He wrote a LOT of the kernel code for the VAX/VMS operating system including some of their security kernel. So far as I know, the VMS O/S has never been cracked from the outside without someone providing valid passwords.

Before anyone jumps my stuff, VMS is eminently crackable if you have a valid login. Insider info will kill every O/S you've got. It is just that you can't crack it using buffer overrun or instruction insertion techniques because of the way their network "broker" tasks work. You can crash the broker - but it is user-mode code that won't kill the O/S if it dies... it just logs you out.

Windows is after all the ultimate framework.

(Remember, this is coming from an atheist...) Please, God, don't let that be true. If THAT is what we face forever, I pity the programmers of the next couple of generations.
 
LOL. And yea and all of the Linux folks are squirming...

Really I was just using that as an example of the framework taken to the extreme. I was around before windows, and back then DOS was the "ultimate framework". In the end it is really about the bios handling the very low level stuff, disks, serial ports, interrupts, memory managent (at the very basic level) and then building up on that to give us devs hard drives, displays, keyboards, mice, memory etc. I remember when we had to understand that stuff.

Now I just open Access and Windows handles all the underneath stuff for Access. And now I just open Access and Access handles all the minute details of tables, fields, indexes, forms, controls and so forth. Access is a framework for databases, on top of the windows framework.

And so why not build a framework on top of Access to handle a bunch of stuff when an App opens, When a form opens. Get the Sysvars which run the framework loaded out of various sysvars tables and cached into memory in classes. I had a sysvar table for the framework itself, for the application and for the client info. The framework sysvars had default values in my Framework library, which were pulled into the FE the very first time that I initialized the framework for a new FE. I could then override the framework sysvars in the fe if this specific fe needed to do something different from the default.

Get logs set up and working. An error log to log errors into tables if the framework sysvars said it was going to go into a table, or a file if the framework sysvars said it was going to log to file.

Then the form side of the house. clsFrm to manage forms as they loaded. Get the various behaviors ready for the form as it loaded.

A framework for me, written by me, to make my Access development easier. It took a couple of years, as I worked on client apps, but it got done and it made my life as a dev in Access much easier.
 
Last edited:

Users who are viewing this thread

Back
Top Bottom