As I said earlier, the implementation of CEnumVariant is too hardcore to show in this book. The code is thoroughly commented, but slogging through it is no picnic. And you don’t really need to understand this code; it’s a gross hack that shouldn’t be necessary. Because you can create collections without understanding the details, why not skip to the next section? If you wanted to understand how vtables work, you wouldn’t have chosen Visual Basic. On the other hand, if you weren’t curious about what’s going on under the surface, you wouldn’t have picked this book.
So I’ll compromise. I’ll tell you a little bit about MEnumVariant (ENUMVAR.BAS) and its partner, CEnumVariant (ENUMVAR.CLS), without showing the source code. You can also check Figure 4-4 for a high-level view.
Chapter 3 explained some of the problems of implementing standard interfaces in Visual Basic. The IEnumVARIANT interface has all these problems and more. (The Next method has the name of a Visual Basic reserved word.) The CEnumVariant class can’t really implement IEnumVARIANT. Instead, it implements IVBEnumVARIANT. At the binary level, these two interfaces are the same; it’s just that IVBEnumVARIANT describes the bytes in a little different way for Visual Basic’s sake. It claims that unsigned integers are signed and tells a few other little white lies. The Windows API type library defines the IVBEnumVARIANT interface, and you can look it up in the object browser.
The IEnumVARIANT interface defines four methods—Next, Skip, Reset, and Clone. The IVariantWalker interface defines three methods—More, Skip, and Reset. What happened to Clone? The Clone method creates a duplicate iterator object with the same state as the current iterator object. You could be iterating through a collection and at some point decide you might want to come back to exactly the same point and try again. You could ask the iterator object to clone itself, and when you’re ready to start over, you could use the duplicate iterator. This would be a very fine thing for clients that want it, but Visual Basic isn’t such a client. It never clones iterators as part of the For Each syntax, and it doesn’t use them when filling the Locals window. Of course, nothing prevents you from using collections created in Visual Basic in client languages that do call the Clone method. In fact, nothing prevents you from writing your own client code in Visual Basic that uses the IEnumVARIANT or IVariantWalker interfaces in more sophisticated ways. But if you think Clone is a really useful feature, you’ll have to implement it yourself. The version in the CEnumVariant class simply raises a “Not implemented” error.
When you hit a For Each block in Visual Basic code, the first thing that happens is that an iterator is created for the collection. Visual Basic calls your collection’s NewEnum function, which creates your iterator (walker) class, which implements IVariantWalker and delegates to a private CEnumVariant object, which implements IVBEnumVARIANT and does some magic to replace the invalid Next and Skip methods with fake methods in the standard module MEnumVariant. The right side of Figure 4-4 shows this part of the transaction. The bottom line is that Visual Basic now has an IEnumVARIANT interface. Fortunately, it has no idea how this baby was made.
Every time through the For Each loop, Visual Basic calls the Next method of its IEnumVARIANT interface, which is actually the BasNext function in MEnumVariant, which calls the ClsNext method of the CEnumVariant object, which calls the More method of the IVariantWalker interface, which is implemented by your walker class, which returns the next Variant in the collection (if any). The left side of Figure 4-4 shows this part of the transaction. In other words, the CEnumVariant object creates events in your iterator class. In an early version of CEnumVariant, I used the Event syntax to communicate between CEnumVariant and the collection iterator object. I created Next, Skip, and Reset events in CEnumVariant, raised them with RaiseEvent in the appropriate CEnumVariant methods, and received them with WithEvents in the collection iterator class. The resulting code was a little cleaner and easier to understand, but it was also about 15 percent slower. Events are great for things that happen occasionally, but you’ll get better performance with Implements if your callbacks are going to be called frequently. I’ll be talking more about events in later chapters.
I suppose the less said about replacing methods in an object vtable the better. This probably isn’t what Visual Basic designers had in mind when they added the AddressOf operator to the language, but there it is. I expect that a few intrepid souls will try even slimier hacks with AddressOf, VarPtr, and CopyMemory. The CEnumVariant class replaces only the vtable entries of the Next and Skip methods because they are the ones whose official definition is incompatible with normal Visual Basic techniques. These two methods must be in a separate standard module (ENUMVAR.BAS) so that they can be used with the AddressOf operator. Check the source file to see how the first parameter of the methods (the this pointer to C++ programmers) actually contains the object pointer of the CEnumVariant object. The Reset and Clone methods are implemented with no tricks in CEnumVariant (ENUMVAR.CLS).
FLAME No need. CEnumVariant is self-flaming code.