There is no mechanism in VBA that would allow you to check if a class exists by it's name and to create a new instance if it is.
However, it is possible to achieve. Let's dissect the problems and see how we can work around them.
Problem 1
You need to create an instance of a class that you are not sure it exists in the current project. How?
As you already tried, Application.Run
and CreateObject
do not work.
Application.Run
is only capable of running methods in standard .bas modules (not class modules). It does not create instances of classes.
CreateObject
does a few things behind the scenes. First, it calls CLSIDFromProgIDEx
using the ProgID (the string) you are passing. Then, it calls CoCreateInstance
. The problem is that VBA classes do not have their ProgIDs in the registry so CreateObject simply doesn't work. You would need to have your class in a registered .dll file of ActiveX.exe to make this work.
That leaves us with the New
keyword to instantiate a new ClassBeta
. That obviously works when the class is present but gives a 'User-defined type not defined' compiler error when it's not. There are only 2 ways to supress this compiler error:
- Have a compiler directive
- Not have the
New ClassBeta
at all
A compiler directive would look like this in ClassAlfa
:
Option Explicit
#Const BETA_EXISTS = False
Sub Test()
Dim myBeta As Object
#If BETA_EXISTS Then
Set myBeta = New ClassAlpha
#End If
Debug.Print "Type of 'myBeta' is: " & TypeName(myBeta)
End Sub
This compiles fine without having the ClassBeta
in the project but you could never make the BETA_EXISTS
conditional compiler constant to turn True
because:
Only conditional compiler constants and literals can be used in expression
So, the last option is not to have the New ClassBeta
anywhere in the project. Except, we do. We can put it in the ClassBeta
itself as a factory:
Option Explicit
Public Function Factory() As ClassBeta
Set Factory = New ClassBeta
End Function
When the class is missing, the factory is missing and the New ClassBeta
will not throw a compiler error.
Problem 2
How do we call the .Factory
method on the ClassBeta
?
Well, we obviously cannot create a new instance of ClassBeta
because that is our first problem.
What we can do is to make sure that ClassBeta
always has a default global instance (like userforms have). To do that we need to export the class to a .cls text file and edit it with a text editor (like Notepad). The text should look like this:
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "ClassBeta"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Option Explicit
Public Function Factory() As ClassBeta
Set Factory = New ClassBeta
End Function
Notice that I've changed the VB_PredeclaredId attribute to True
(manually, in the text editor). Now we can import the class back. We can check if it worked by typing ?Typename(ClassBeta.Factory)
in the Immediate window and then pressing Enter. We should see:

The code in ClassAlfa
can now be written as:
Option Explicit
Sub Test()
Dim myBeta As Object
On Error Resume Next
Set myBeta = ClassBeta.Factory
On Error GoTo 0
Debug.Print "Type of 'myBeta' is: " & TypeName(myBeta)
End Sub
Problem 3
If we remove ClassBeta
from the project, the following line does not compile:
Set myBeta = ClassBeta.Factory
But, compared to problem 1, this time the compiler error is 'Variable not defined'.
The only way that I could think of, to get rid of this new compiler error, is to turn off Option Explicit
. Yeah! That bad!
ClassAlfa
:
'Option Explicit 'needs to be off to be able to compile without the ClassBeta class
Sub Test()
Dim myBeta As Object
On Error Resume Next
Set myBeta = ClassBeta.Factory
On Error GoTo 0
Debug.Print "Type of 'myBeta' is: " & TypeName(myBeta)
End Sub
Final Thoughts
You could do the development while Option Explicit
is on and turn it off for your users.
If your users will use the ClassAlfa
with Option Explicit
off, the the code should work fine. But if they want ClassBeta
as well, then the only way they could get it would be via importing the .cls file. Copy-pasting code won't work because they would lose the global instance of ClassBeta
(the one we set in the VB_PredeclaredId
hidden attribute).
I must say that I do not recommend removing Option Explicit
because that could lead to other issues. I do not think there is a way to achieve what you want in a 'clean' way.