by Dan Appleman - Desaware
Visual Basic has long been the easiest method to use in developing applications for Microsoft Windows. But with VB5, it is safe to say that Visual Basic is no longer "easy". With this latest release, Visual Basic has been rebuilt from the ground up to be based internally on OLE (now called ActiveX) technology. This process began with VB4, but is now much more visible to the typical Visual Basic programmer. This fact is especially important for those developers who will be creating ActiveX components (and if you are not, you probably should be).
A complete and in depth explanation of OLE and ActiveX technology from the Visual Basic programmer's point of view can be found in my new book: "Dan Appleman's Developing ActiveX Components with Visual Basic 5.0: A Guide to the Perplexed" which should be available in early April. I will use the brief space I have available here to cover a few key points. The first thing to remember is that an interface is a contract - it represents a set of functions that an object exposes to the world. An object can expose more than one interface - in which case the software using the object can "ask" for a particular interface to use.
Every interface in COM (the Component Object Model on which OLE is based), contains within it an interface called IUnknown which is used to keep track of the number of references to an object, and to request a different interface from an object. There is another type of interface, called an "automation" interface or "IDispatch" interface which allows an object to publish a list of properties and functions at runtime which can then be executed by programs using the object. Automation interfaces are very flexible, but do not support every possible type of variable - for example: they do not support user defined types. This is why a public Visual Basic class function cannot have a user defined type as a parameter.
When you create a form, class, ActiveX control or ActiveX document, you create an object that has a main interface which contains the functions and properties that you define for the object. You can also use the Implements statement to have your object support additional interfaces. But keep in mind that all of these interfaces are automation interfaces. You cannot use Visual Basic to create non-automation interfaces, and cannot add non-automation interfaces to your object using the Implements statement. Visual Basic does, however, use non-dispatch interfaces internally. It must, because many of the features offered by Visual Basic including OLE Drag/Drop and ActiveX controls make extensive use of standard non-dispatch interfaces.
Let's take a look at three different interfaces and what they mean to Visual Basic programmers:
Example 1: IPerPropertyBrowsing
Most Visual Basic programmers take the Visual Basic property page a little bit for granted. It is a bit amazing when you think about it. How can the property page populate itself with the properties for any ActiveX control? How does it know to display the word (Picture) or (Empty) for a picture control? How does it populate the drop down list box for enumerated properties?
It can do these things because ActiveX controls provide information about their properties through built interfaces. One of these interfaces, named IPerPropertyBrowsing, is responsible for allowing the Visual Basic property page (or any container) to retrieve a "display name" for the property - what it should display as the property value if the control wants to display something other than the value. It is also responsible for populating the drop down list for enumerated properties. This is an important issue for control developers.
To illustrate, you wish to display an enumerated property that can consist of three colors, red, green and blue. You could create an enumerated property:
Public Enum ColorList Red = 1 Green = 2 Blue = 3 End Enum
But this would be a bad idea. Why? Because it is very possible that another control will expose the same enumerated names with different numbers, thus leading to either a value conflict, or a subtle and hard to find bug. That is why you should always prefix the constant value with a product or company prefix that will help make your constants unique. For example:
Public Enum ColorList dwRed = 1 dwGreen = 2 dwBlue = 3 End Enum
This approach is better for programming and will cause the following to appear in the drop down list box when a developer uses the Visual Basic property window to configure your control.
However, the Visual Basic property window is the one place where you probably do not want the constant names - you might want it to read:
In order to do this, you need the ability to override Visual Basic's default implementation of the IPerPropertyBrowsing interface. SpyWorks 5 lets you do so, thus taking control of both the display name and the enumeration names for any properties in ActiveX controls that you develop.
Example 2: IObjectSafety
An important aspect of distributing your ActiveX controls over the internet and intranets relates to security. ActiveX controls have full access to all of the capabilities of your system, and thus have the ability to create havoc. Microsoft advocates a three tiered approach towards object safety for networks:
What does it mean to make an object safe? It means that the object cannot cause harm to the end user's system. There are two issues to resolve: Is the object safe to load and initialize? Is it safe to program? Safe to initialize means that the ActiveX control can be placed on a web page and will be safe regardless of the initial property values that are loaded from the page. Safe to program, (also known as "safe for scripting") means that the object will do no harm to an end user's system regardless of what the controlling program attempts to do. The control must be able to handle any possible function call and property setting safely. The degree of safety of an object depends entirely on the object's author.
Microsoft defines two ways to mark a control as safe. The method utililized by VB5 is to add an entry to the registry section for the control describing it as safe to initialize or safe for scripting. This approach has two disadvantages: it requires that the control always meet the safety level marked, and it requires an extra registry look up each time the control is loaded. The second approach is to expose an interface called IObjectSafety. This interface contains functions that allow the container to request the control to enter safe for initialization or safe for scripting mode - as needed. The control can then respond with the degree of safety that it can actually support. This flexible approach to object safety is not supported by VB5 alone, but can be implemented in less than ten lines of code using SpyWorks 5.
Example 3: IShellLink
IShellLink is an interface that is exposed by the Windows 95 and Windows NT 4.0 interfaces to allow you to create, modify, and read file manager short cuts - files that typically have the extension .LNK. Like other non-automation interfaces, you can't work directly with this interface using Visual Basic 5.0, but the SpyWorks dwEasy control exposes an object that lets you work directly with the IShellLink interface. SpyWorks 5 will continue to add support for interfaces of this kind. One of the most important of these included with SpyWorks 5 is the Desaware Enumeration object. This object supports the IEnumVariant interface, allowing you to add support for the Visual Basic For...Each command to any object, array or collection. This can be dramatically more efficient than the technique demonstrated in the VB5 documentation, where every custom collection contains a separate collection object. Now your collections can be based upon arrays or linked lists, still use enumerations and work with the For...Each command.
This article has detailed three different types of interfaces and the problems that they present. In one example it was necessary to override a portion of an existing implementation of an interface that is currently supported by Visual Basic - while leaving the rest of the implementation untouched. In another example it was necessary to add a completely new non-automation interface to an existing object. In the third example it was necessary to access an object using a non-automation interface. The SpyWorks 5 ActiveX Extension Library tackles all three of these problems, in some cases with solutions specific to standard interfaces, in others with generic solutions that you can extend in ways that we can't even imagine - and you can expect the number and types of interfaces supported by SpyWorks to increase as time goes on.