07 January 2026

Set and reference counting

You're probably familiar with the Set command for working with objects. In GB32, Set plays a crucial role when working with COM (Component Object Model) objects, and understanding what happens behind the scenes will help you write more reliable code.

What is a COM Object?
Think of a COM object as a box sitting somewhere in your computer's memory. Inside that box are two things:

  • A reference counter - a simple number that tracks how many variables are currently "pointing to" this box
  • The actual data and methods - the useful stuff your object does

When you create a COM object in GB32, what you actually get is a pointer - essentially an address that tells your program where to find that box in memory.

COM Variables Are Pointers
When you declare a COM variable in GB32:

Dim MyForm As Form

You're not creating the 'box' or Form itself. You're creating a variable that will point to the box. The variable contains just an address, not the entire object. At this point, MyForm doesn't point to anything yet - it contains Nothing.

To actually create the Form object, you use the Form command:

Form MyForm = "My Window", 100, 100, 640, 480

This single command does two things: it declares the variable MyForm and creates the actual Form object in memory, with the variable pointing to it. The Form now exists with the title "My Window" at position (100, 100) with dimensions 640×480.

The Reference Counter
The reference counter inside the COM object is like a visitor log. Every time a new variable points to the object, the counter should go up by one. Every time a variable stops pointing to it (goes out of scope or gets reassigned), the counter should go down by one. When the counter reaches zero, the system knows nobody is using the object anymore and it's safe to destroy it and free up that memory.

Interestingly, when you create a Form object, its reference counter starts at 2, not 1. This is because two variables point to it: the variable you declared (MyForm in our example) and the special Me keyword, which always points to the Form object from within its own event handlers and methods.

How Set Works
The Set command does two important things:

Dim MyForm As Form
Set MyForm = Win_1
  1. If MyForm was already pointing to something, it tells that old object "I'm done with you" (decrements its reference counter, unless it is already 0)
  2. It makes MyForm point to the new object (Win_1) and tells that object "I'm using you now" (increments its reference counter)

This keeps the reference counting accurate so the system knows which objects are still in use.

You can also use Set with the special keyword Nothing:

Set MyForm = Nothing

Nothing means "point to no object at all" - it's like erasing the address from your variable. When you set a COM variable to Nothing:

  • If MyForm was pointing to an object, it tells that object "I'm done with you" (decrements its reference counter)
  • MyForm now contains no valid address - it's empty and safe to check with If MyObject Is Nothing

Setting variables to Nothing when you're done with them is good practice. It explicitly releases your reference to the object, potentially allowing the system to free up memory sooner rather than waiting for the variable to go out of scope. It also makes your code clearer by showing when you're intentionally finished with an object.

Set MyObject = CreateObject("Some.Component")
' Use MyObject...
Set MyObject = Nothing  ' Done with it now

Passing COM Variables to subroutines
Here's where GB32 does something interesting. When you pass a COM variable to a procedure. Here is test program that allows you to obtain the current reference count of an object. The RefCount() function takes the address of COM variable and returns the current number of variables that reference the COM object:

Form frm1 = "RefCount example", 30, 30, 400, 400

Print "After creation RefCount = "; RefCount(V:frm1)
testbyval frm1
testbyref frm1

Do
  Sleep
Until Me Is Nothing

Proc testbyval(frm As Form)
  Print "In testbyval RefCount = "; RefCount(V:frm)
EndProc
Sub testbyref(frm As Form)
  Print "In testbyref RefCount = "; RefCount(V:frm)
EndSub

Function RefCount(ByVal ComVarPtr As Intptr) As Long

  Local Intptr ComPtr = {ComVarPtr}     // address of COM object
  Local Intptr ComPtr_Vtbl = {ComPtr}   // address of the vtbl
  Local Intptr ComPtr_AddRef = {ComPtr_Vtbl + 4}  // address of AddRef
  Local Intptr ComPtr_Release = {ComPtr_Vtbl + 8} // address of Release

  // Increment reference count and obtain the new number of references
  ~StdCall(ComPtr_AddRef)(ComPtr)       // pass the this pointer
  RefCount = {ComPtr + 4} - 1           // the new count - 1
  // Immediately decrease the reference count
  ~StdCall(ComPtr_Release)(ComPtr)      // pass the this pointer

EndFunc

The testbyval procedure shows that GB32 makes a copy of the pointer (so the function has its own variable pointing to the same box), but it does not increment the reference counter. This means the counter doesn't reflect that the function is also using the object.

In most cases, this works fine - your original variable keeps its reference alive, so the object stays valid while the function runs. However, if something unusual happens during the function (like the original variable getting reassigned or the function triggering code that releases the last reference), the object could be destroyed while the function is still trying to use it.

This is something to keep in mind when writing complex code, particularly code that might call back into other parts of your program or handle Windows messages while a function is executing.

Summary

  • COM objects live in memory and contain a reference counter
  • COM variables are pointers to these objects
  • Set properly manages the reference counter when assigning objects
  • Nothing is used to clear a COM variable and release its reference
  • Passing COM variables to functions creates a copy of the pointer without updating the counter
  • The reference counter helps the system know when it's safe to clean up unused objects

Understanding these mechanics will help you write more robust GB32 applications, especially as your programs grow in complexity.