I'm going to give a step by step example of creating and using ActiveX DLL's. We'll use a DLL called DogClass, with two classes: Dog and Puppy. Puppy will be an example of a dependent class in a class hierarchy.
We'll start by creating a new ActiveX DLL Project. Change Class1's Name property to Dog. Change the Project name to DogClass. Save the Project.
Next, add the following code to the Dog class window:
Option Explicit 'in General Declaration Public Size As Integer
Public Sub Bark() If Me.Size = 1 Then MsgBox "WOOF WOOF" Else MsgBox "warf warf" End If End Sub
Save your work. You now have a usable, bare-bones DLL. You can compile this, set a reference to the DogClass DLL from another project, instantiate a Dog class, set the size property (it will default to 0 if you don't set it, of course), and call the Bark method. If you like, go ahead and try that.
However, we're going to use a test environment, that will allow you to step through code in the client and the DLL simultaneously. To do this, first add Standard EXE project to your DogClass project (File/Add Project). Name this project TestDogClass, and I prefer to name the form frmDogClassTest and caption it "Test Dog Class." To this form, add a command button called cmdTestDogClass and caption it appropriately. In the Project Explorer window, right click on the TestDogClass project and select "Set as Start Up." This line should become boldfaced. Save your work.
Now, making sure that you have something in the TestDogClass project highlighted (as opposed to the DogClass project) in the Project Explorer window, go to References. You should see DogClass near the top. Notice also that DogClass.vbp is listed as the source file. (If you were to remove the DogClass project, and a compiled version existed of it, VB would fix up the reference to DogClass.dll.) Select DogClass. Now, add the following code to the frmTestDogClass code window:
Option Explicit 'General Declarations section Dim Fido As DogClass.Dog Dim Fifi As DogClass.Dog
Private Sub Form_Load() Set Fido = New DogClass.Dog Fido.Size = 1 Set Fifi = New DogClass.Dog Fifi.Size = 0 End Sub
Private Sub cmdTestDogClass_Click() Fido.Bark Fifi.Bark End Sub
Save your work. You'll find you're prompted to save a file called Groupl.grp. Change this to DogClassGroup and save. When you revisit this project, open the group file, not one of the individual projects files. You'll open both of the projects as they are now instead of just one or the other.
Now, run the program, and hit the command button. You should see a big bark and a little bark. If you like, set a breakpoint on the Form_Load event and step through the code, to see what gets called when.
You now have a bare bones class and the testing mechanism for same. Let's add a few enhancements to show some other ideas.
First, let's add an Enum to make the Size property look nicer, and restrict its values to allowable ones. In the General Declarations section of the DogClass code module, comment out the Size property declaration and add the following:
Enum DogSize Big = 1 Little = 0 End Enum Public Size As DogSize 'Public Size as Integer
Now, change the Size property assignments and Bark method as follows:
Private Sub Form_Load() Set Fido = New DogClass.Dog Fido.Size = Big Set Fifi = New DogClass.Dog Fifi.Size = Little End Sub
Public Sub Bark() If Me.Size = Big Then MsgBox "WOOF WOOF" Else MsgBox "warf warf" End If End Sub
The Enum has the effect of restricting allowable values to 0 and 1.
Now, let's look further at the Size property. A public variable is fine, as far as it goes. But if you need more control, you'll need to use property procedures. For example, if you want to raise custom errors to developers setting properties incorrectly, you'll need them. Also, if you want to create a read-only property, you'll need them.
To create a property using property procedures, there are three steps: 1. Create a module-level variable (any name you like) to hold the current value of the property. 2. Create a Property Let procedure that sets the value of this variable. 3. Create a Property Get procedure that returns the value of this variable. If you want to create a read-only property, just omit the Let procedure. (For completeness's sake, if the property value is itself an object, you use a property Set procedure instead of a property Let procedure.) In our case, change the code in the DogClass code window as follows:
'Public Size As Integer 'Public Size As DogSize Private cSize As DogSize 'You can use Dim here too
Public Property Get Size() As DogSize Size = cSize End Property
Public Property Let Size(x As DogSize) cSize = x End Property
So, cSize holds the current value of the property, and the Get and Let property procedures set and return, respectively, the value of cSize.
Next, let's build a dependent object, Puppy. A dependent object is one that can't be directly instantiated by a client. Instead, it's instantiated by calling some method of another class that is itself instantiable, that returns an instance of the class. A well-known example of this is ADO's Field object, which can't be directly instantiated. Rather, you have to instantiate a recordset and open it, and reference the resulting Fields collection of the recordset.
To create the Puppy class, first add a Class to the DogClass project. Name it Puppy. Change (and this is important) the Instancing property to PublicNotCreateable. In this way, clients won't be able to create an instance of Puppy directly. Now, add the following Bark method code to the Puppy class:
Public Sub Bark() MsgBox "yip yip" End Sub
And, add the following Puppy instantiation code to the Dog class:
Public Function CreatePuppy() As Puppy Set CreatePuppy = New Puppy End Function
Note that we don't have to specifically declare a Puppy variable in this context before using the New keyword. In effect, we've done this by returning a variable of type Puppy.
Change the code in frmTestDogClass to look like this, adding Puppy support:
Option Explicit Dim Fido As DogClass.Dog Dim Fifi As DogClass.Dog Dim Rover As DogClass.Puppy
Private Sub Form_Load() Set Fido = New DogClass.Dog Fido.Size = Big Set Fifi = New DogClass.Dog Fifi.Size = Little Set Rover = Fido.CreatePuppy End Sub
Private Sub cmdTestDogClass_Click() Fido.Bark Fifi.Bark Rover.Bark End Sub
Save your work, and run it.
Finally, let's do one more thing. We want to know the parent of the puppy, and make it bark in accordance with the parent's size. Now, the clean way to set up the Parent property is to maie it read only, setting it to a reference to the instantiating Dog class at the point of instantiation. This is the sort of thing that constructors are generally used for. In VB6, you have to find another way. With a little work, you can do the same thing.
First, create a read-only property Parent in the Puppy class module, of type Dog. Now, you only want to be able to set this property from the CreatePuppy method of the Dog class. As such, you need to access the Private variable in the Puppy class that holds the current value of the Parent property (we'll use cParent for the name), which you can't do directly. So, how do we do it? We don't want to make cParent public, since then it won't be read only anymore. So, we should create a Sub in Puppy that sets cParent. But, if it's Private, we can't call it from CreatePuppy, and if it's Public, anyone can call it. So, this is where Friend comes in. We want to create a method in Puppy called SetParent, which takes an argument of type Dog which is then assigned to cParent. And, we want this method to be visible throughout the Project, but not to clients of the classes in the project. This is Friend.
So, change the existing code as follows. In Puppy, add the read only Parent property, and evaluate it in the Bark method:
Private cParent As Dog
Public Property Get Parent() As Dog Set Parent = cParent End Property
Friend Sub SetParent(x As Dog) Set cParent = x End Sub
Public Sub Bark() If Me.Parent.Size = Big Then MsgBox "yap yap" Else MsgBox "yip yip" End If End Sub
In Dog, call the SetParent sub from the CreatePuppy method, passing a reference to the calling Dog instance as the argument:
Public Function CreatePuppy() As Puppy Set CreatePuppy = New Puppy CreatePuppy.SetParent Me End Function
In the Test module, add another Puppy, and set one parent to the big dog and one to the little:
Option Explicit Dim Fido As DogClass.Dog Dim Fifi As DogClass.Dog Dim Rover As DogClass.puppy Dim Lassie As DogClass.puppy
Private Sub Form_Load() Set Fido = New DogClass.Dog Fido.Size = Big Set Fifi = New DogClass.Dog Fifi.Size = Little Set Rover = Fifi.CreatePuppy Set Lassie = Fido.CreatePuppy End Sub
Private Sub cmdTestDogClass_Click() Fido.Bark Fifi.Bark Rover.Bark Lassie.Bark End Sub
All right. This should give you a good grounding in the fundamentals of ActiveX DLLs.