Dual Interfaces and IDispatch


When you call a method or property, it will work in different ways, depending on a factor that is mostly beyond your control. Some OLE classes have IDispatch interfaces, some have COM (sometimes called vtable) interfaces, and some have both (dual interfaces). From Visual Basic’s viewpoint, IDispatch represents evil and COM represents virtue. Dual interfaces represent a pact with the devil to make do in a world of sin, but you should never need the darker IDispatch side.


This doesn’t mean IDispatch is evil in general. You still need it for VBScript and other environments that always do late binding. Some authorities will claim that there are realistic situations where you don’t know a server’s name until run time and therefore you will need late binding. For example, you might have two polymorphic servers that provide the exact same services in two different ways. You have to ask your user (or perhaps ask an external database or a Web site) whether to use the UglyFast server or the PrettySlow server. These might be completely different servers running on different remote machines. You could write code like this:

If fFastAtAnyPrice Then
Set obj = CreateObject(“UglyFast.CJumpInTheLake”)
Else
Set obj = CreateObject(“PrettySlow.CPlungeIntoThePool”)
End If

I have yet to see a practical example of this situation under Visual Basic, but I’m told that there are some. Even in cases where you need CreateObject, it might still be possible to get early binding with the new Implements statement.


The IDispatch interface was designed for late-bound objects. It works (and works much faster) with early-bound objects, but it’s less flexible. It still filters all arguments through Variants and it still does a certain amount of parsing and general thrashing to find the addresses and specify the arguments of methods and properties. Some of the information it finds is already known by the type library.


The COM interface, by contrast, is simple and direct. It consists of a simple table of addresses for each of the methods and properties. When you call a method, Visual Basic looks up its address and calls it. There are only two steps, and they translate into very few assembly language instructions. The table that makes this possible is called a vtable, and it has the exact same format as the vtable that makes virtual functions possible in C++. The tradeoff is information. If you have information, you construct a vtable and make fast calls through it. If you don’t


Problem: If you choose a class solution over an equivalent functional solution, what performance penalty, if any, must you accept? There’s no easy one-to-one comparison, but the following information might give you a clue about the relative performance of methods, procedures, properties, and variables. As a bonus, you get a comparison of creating objects with New in the declaration versus using Set with New.

Problem P-Code Native Code
Call method function on object 0.0476 sec 0.0090 sec
Call method function on New object 0.0466 sec 0.0096 sec
Call method function on late-bound object 1.0185 sec 0.7828 sec
Call private function 0.0438 sec 0.0071 sec
Pass variable to method sub on object 0.0268 sec 0.0055 sec
Pass variable to method sub on New object 0.0287 sec 0.0071 sec
Pass variable to method sub on late-bound object 0.8585 sec 0.6980 sec
Pass variable to private sub 0.0384 sec 0.0088 sec
Assign through Property Let on object 0.0232 sec 0.0094 sec
Assign through Property Let on New object 0.0252 sec 0.0057 sec
Assign through Property Let on late-bound object 0.7661 sec 0.6229 sec
Assign through private Property Let 0.0176 sec 0.0014 sec
Assign from Property Get on object 0.0327 sec 0.0069 sec
Assign from Property Get on New object 0.0301 sec 0.0068 sec
Assign from Property Get on late-bound object 0.8045 sec 0.6790 sec
Assign from private Property Get 0.0172 sec 0.0005 sec
Assign to public property on object 0.0068 sec 0.0020 sec
Assign to public property on New object 0.0058 sec 0.0019 sec
Assign to public property on late-bound object 0.8582 sec 0.7918 sec
Assign to private variable 0.0032 sec 0.0003 sec
Assign from public property on object 0.0079 sec 0.0018 sec
Assign from public property on New object 0.0090 sec 0.0016 sec
Assign from public property on late-bound object 0.9445 sec 0.8459 sec
Assign from private variable 0.0035 sec 0.0004 sec


Conclusion: Hard to say. Using classes and objects definitely has a performance cost. Classes that wrap very simple operations might cost more than they are worth, but for most classes, the overhead is tiny compared to the total cost of what the class actually does.


have information, you look up that information the hard way and make slower calls. If you have the information but don’t use it, you’re wasting your time.


So if COM servers with dual interfaces are better than those with IDispatch interfaces, where do you find dual interfaces? The answer is: almost everywhere. For example, you’ll always get them from Visual Basic. In fact, there are only two development tools I know of that create dispatch-only ActiveX components. Unfortunately, one of them is probably the most common tool for creating components: the Microsoft Foundation Class (MFC) library. The other is the Delphi programming environment (version 2). Both Microsoft and Borland are moving to new tools that create dual interfaces.


Lest you think that MFC creates slow, fat COM objects, let me clarify one thing. What has the greatest effect on the speed of any component is its slowest part. If COM calls through IDispatch are the slowest part of a component’s operation, then you can blame the provider of that component. Generally, the bottleneck for controls is window creation and management. The bottleneck for COM EXE servers is transfer across process boundaries through a process called marshaling. You’ll see performance examples demonstrating this in Chapter 10. The Performance sidebar in Chapter 10 illustrates that a Visual Basic DLL component compiled to native code is significantly faster than an equivalent C++ MFC component. Dual interfaces, not the programming language or the compiler, make the difference.