What follows are an assortment of topics, many of them frequently overlooked, to consider when building your own ActiveX components.
There is nothing to stop you from including form modules within your ActiveX EXEs and DLLs. However, you should distinguish between an ActiveX component that is designed to run on the client machine and one that will be a remote server.
The rule is that ActiveX remote server components shouldn't contain any UI what-soever—not only no forms, but no message boxes either. The reason for this is that the UI appears on the remote machine, not on the client machine. You can imagine the uselessness of a message box popping up on some remote application server stuck away in a locked machine room, waiting for someone to click OK!
Allowing clients to use the ForEach...Next statement
Most of the time, we take the For Each...Next loop for granted as it iterates the members of an array or a collection. It's the fastest, most efficient method of visiting all the members of the collection or array, and we could care less that, by enumerating the members of the collection, the unseen code is actually gener-
72 Chapter 4 - Class Modules ating new references to members of the collection with each iteration of the loop. However, as the provider of a collection class, it's up to you to provide an interface that the For Each...Next statement can work with.
This may sound a little daunting, but you'll be pleasantly surprised by how easy it is to implement a property that enumerates members of the collection within your class. First of all, you must create a Property Get procedure called NewEnum with the type of IUnknown. Its syntax is always the same:
Public Property Get NewEnum() As IUnknown
Set NewEnum = mCol.[_NewEnum] End Property where mCol is the name of your private collection object variable.
Second, set the Procedure ID for this Property Get procedure to -4. To do this, select the Procedure Attributes option from the Tools menu, then click the Advanced button on the Procedure Attributes dialog. Enter a Procedure ID of -4. You should also check the "Hide this member" option to prevent the property from appearing in the IntelliSense drop-down list.
For more information about using the For Each...Next statement in a client application, see its entry in Chapter 7.
More care and thought than normal needs to go into handling errors within a class module. In general you shouldn't do more than pass on the error to the client with the Err.Raise method. However, if your error handling code is going to pass the error number back to the client, you need to decide if the error number should have the vbObjectError constant added to it or not. (vbObjectError is a constant that allows a referencing object to determine that the error was generated in a VB class object.) Another consideration is whether or not and how the Err.Source property is passed back to the client.
A full discussion of error handling and the Err object, including class module error handling, can be found in Chapter 6, Error Handling.
One last point to note is that you should never use the End statement within a class module.
Use Dictionary objects rather than Collection objects
When you're creating object models, many of your ActiveX server components will be based upon collections of other objects. Ever since VB4, developers have become accustomed to writing these classes based on the Collection object (in effect, creating a wrapper for the Collection object), taking for granted the fact that the Collection object is slow and expensive in terms of overhead, but also knowing that there was no real alternative. However, if you're using VB6 to create collection classes, I strongly recommend that you use a Dictionary object in place of the Collection object. The Dictionary object is fast in comparison to the Collection, and it has more functionality. For a complete explanation of the Dictionary object, see the Dictionary Object entry in Chapter 7.
Creating ActiveX Components 73
Provide your own Exists property in collection classes
Collection objects are neat for quickly accessing values and objects given a key value. You can add items to the collection, retrieve a given item from the collection, find out how many items are in the collection, and remove an item from the collection. For example, this simple code retrieves an object from a collection using its key:
Public Property myClass(vVal as Variant) As myClass
Set myClass = mCol.Item(vVal) End Property
However, if the key vVal doesn't exist in the collection, you're faced with a runtime error. The gap in the usability of the Collection object is that there is no Exists property. But you can easily plug this gap and provide a way to add a missing property to the collection as follows:
Public Property Exists(sVal as String) As Boolean On Error Goto myClass_Err Dim oTest As myClass Set oTest = mCol.Item(sVal) Set oTest = Nothing Exists = True Exit Property TryToGetIt:
If getItemforCollection(sVal) Then Exists = True
Exists = False End If
Exit Property MyClass_Err:
If Err.Number = 5 Then
Resume TryToGetIt Else
'over-simplified error handler! Err.Raise Err.Number + vbObjectError End If End Property
Quite simply, you attempt to assign the collection to a test object. If the assignment works, return True; otherwise, handle the error by calling a function, getltemforCollection, that adds the new item to the collection. If getltemforCollec-tion returns False, however, this means that the item doesn't exist, and the Exists property must return False. Using the Exists property, you can preface each call to get an object with a conditional statement like the following:
Set oMyClasses = New myClasses
If oMyClasses.Exists("xyz") Then
Set oMyClass = oMyClasses.MyClass("xyz")
MsgBox "Sorry it doesn't exist!" End if Set oMyClass = Nothing
74 Chapter 4 - Class Modules
When is an in-process component out-of-process?
Until recently, you could say with certainty that an ActiveX EXE would run out-of-process, whereas an ActiveX DLL would run in-process. However, if you were now asked if a particular component was in-process or out-of-process, you would have to respond with the question, "Whose process?"
Technically, ActiveX DLLs will always be in-process, but traditionally (if you can call a couple of years a tradition!) the "process" referred to is that of the client application—but not any more! This shift in thinking has come about because of new technologies such as Distributed COM (DCOM), Microsoft Transaction Server (MTS), and Microsoft Distributed Transaction Coordinator (MDTC), which are blurring the traditional process boundaries.
For example, an ActiveX DLL running in MTS is running within the MTS's process, but it's running outside of the client application's process. In fact, boundaries are becoming blurred to such an extent that to the user of the client application, the old boundaries are for the most part completely invisible. During the development of a client-server application recently, I was aware that one of my DLLs was executing, only I wasn't quite sure which one of three machines it was executing on!
If the ActiveX object you need to reference provides a type library and is registered on your computer, you can create a reference to the component using the References dialog. This includes the component's properties, methods, and events in the Intellisense drop-down list and provides statement completion for its properties, methods, and events. Furthermore, this enables you to early bind to the component's classes.
If the ActiveX object either doesn't provide a type library, or you don't know at design time which classes of a component you will need, you should use late binding.
Was this article helpful?