As previously mentioned, every component or control you create relies on interfaces of some sort . At the very least, a component or control must implement the IUnknown interface. An interface is a technique for bundling methods in a way that's independent of programming language. When a client needs access to an interface that it knows exists on the server, it queries that interface and accepts a pointer to it. The client uses the interface by calling the methods that it contains.
Most development languages today hide the gory details of interface interaction from the programmer. In most respects, the simplification of interface use is good because it boosts developer productivity and reduces the number of bugs in the resulting application. However, one problem with this approach is that many developers don't realize that interface access is also a process of manipulating pointers. You saw how important it was to know this when working through the Microsoft Visual C++ examples in this book, which showed that Visual C++ still requires thorough knowledge of interface operations.
Whether or not you know how an interface works, the concept of the interface is still important. You need to know which interfaces a component provides to interact with that component. Microsoft Visual Studio provides a handy utility, named OLE/COM Object Viewer, which you can use to view these interfaces in more detail. (Some releases of Visual Studio shortened the name to OLE View). We'll use this utility several times in this chapter, so you might want to install it if you haven't done so already.
As your copy of Visual Studio gets older, so do the tools that came with it. In many cases, you'll want to obtain updates for those tools as needed to ensure the tool provides everything needed-this is especially true of the OLE/COM Object Viewer because Microsoft updates this tool regularly. You can obtain this update from the main COM Web site at the URL http://www.microsoft.com/com/resources/downloads.asp . This section of the chapter uses the latest version of the OLE/COM Object Viewer at the time of writing (version 2.10.059). Some screen shots in this book might look slightly different from the ones you'll see when using an older version of the product. Because the latest version of the OLE/COM Object Viewer fixes several important problems with previous versions of the product, you'll want to download and install the latest version of the product as quickly as possible.
It's important to understand how the OLE/COM Object Viewer can help you during the development process. Suppose you want to find out about the interfaces provided by the MyMath component that we've discussed several times in this book. The OLE/COM Object Viewer could help you find out about those interfaces and the associated entries in the registry. Even if you're familiar with the registry entries for an unmanaged component, it pays to look at the managed component entries because they're different.
The version of the MyMath component used in this section is the one found in the Chapter11\MyMath\bin\Debug folder of the book's companion content. If you use one of the other versions, the interfaces will vary from those shown in this section. You also need to unregister previous versions of the component that you might have installed so that you don't confuse it with the target component.
Some developers wonder how this information will actually help reduce development time or provide some other performance gain. Knowing how the entries differ can help you locate problems later in your applications. You could use this information to debug problems with the way that a component (or other COM object, such as a document server) registers itself. Obviously, this information also comes in handy to develop a better view of how the component is put together. As previously mentioned, you need to know what the component can provide before you can use it in an application.
For the .NET developer, the OLE/COM Object Viewer also provides a window into existing components . For example, we discussed the IComponent interface used in the typical MMC snap-in. Your workstation includes a number of MMC snap-ins you can use as templates. Analysis of these templates would help you understand that the MMC snap-in requires the IComponent interface. Research into this interface would demonstrate that you need to build it for the .NET environment.
Strange things can happen if you create an instance of an application or component in the OLE/COM Object Viewer and then don't release it. For example, your computer might freeze unexpectedly. Every time you view the interfaces supported by an application or component, you have to create an instance to do it. You can tell whether there's an instance of an object by looking at the application name. The OLE/COM Object Viewer displays any open objects in boldface type. To release the instance of the object you created, right-click on the object name (such as XYZ Single Document), and then choose Release Instance from the context menu. Fortunately, the OLE/COM Object Viewer is good about closing instances of objects before you leave them, but you might need to take the aforementioned steps during a viewing session if your machine begins to run out of memory. Remember that every instance you create also uses some memory.
Open the OLE/COM Object Viewer. (You can use the option on the Tools menu of the Visual Studio .NET IDE to perform this task.) You'll see a set of folders that encompass the various types of objects. It will look similar to the one shown in Figure 14-1. Notice that these statically defined classes are rather broad. There's a very good chance that a component or other type of COM server could appear within more than one folder, depending on which interfaces it implements.
Notice that the OLE/COM Object Viewer includes a special .NET Category entry. Open this folder, and look through the list to find the MyMath.MathFunctions class. Open the class, and you'll see a list of interfaces like the one shown in Figure 14-2. This is the first time you've actually seen all the interfaces the MyMath.MathFunctions class supports.
Compare the list of interfaces shown in Figure 14-2 to the one in Figure 14-3. The Component Services console shows only the interfaces COM+ thinks you'll find interesting. It hides interfaces such as IUnknown because there's no reason to display them as part of a COM+ application. However, there's good reason to know that the interfaces exist. For example, notice in Figure 14-2 that the MyMath.MathFunctions class supports the ISupportErrorInfo interface. When you look up this interface in the documentation, you'll learn that all automation objects must support it. In addition, this interface enables propagation of error information as needed to the component-this is the source of error information received from an unmanaged source.
Look at the IMathFunctions interface in Figure 14-2. The entries in the right pane tell you about the registry entries for this interface. For example, you'll find out that the registry indicates the component has a threading model of Both . In addition, you'll see the descriptive text provided for this interface and the location of the type library that holds it.
With the IMathFunctions interface selected in the OLE/COM Object Viewer, click the View button. You'll see a short description of the IMathFunctions interface. Click View Type Library, and you'll see a description of the IMathFunctions interface like the one shown in Figure 14-4.
As you can see, this view lays out all the information you might want to know about this interface. This information becomes important as you decipher the intricacies of a component that you want to model. Notice that the information you receive includes variable type and even variable name information.
Whenever you finish using a component, remember to release it. You can do this by right-clicking the MyMath.MathFunctions class entry and choosing Release Instance from the context menu. The interface listing will close, and the OLE/COM Object Viewer will remove the highlight from the entry. (The OLE/ COM Object View will list in boldface type all classes that you instantiate.)
The previous section helped you see some intricacies of a managed component type. In this section, we look at an unmanaged control. Viewing an unmanaged control will provide additional insight into the use of the OLE/COM Object Viewer. Open the OLE/COM Object Viewer if you haven't done so already.
Let's look at a control. Open the Controls folder, and then open the Microsoft Forms 2.0 CommandButton control (or another control if you don't have this component registered on your machine). You'll see the list of interfaces shown in Figure 14-5.
The interfaces tell you a lot about the support that the control provides. For example, notice that the control includes the IAccessible interface to enable accessible devices to learn more about the control. Like the managed component we discussed earlier, the component entry contains a wealth of information. In this example, the information includes the following:
Implementation details (such as the location of files and threading model)
Activation details (such as the remote activation site when required)
Launch permissions (such as who can run the application)
Access permissions (such as who can look at the component's settings)
All these settings have default values based on what the developer specified during the development process. Viewing these details can help you use the control as a template for developing controls of your own. Comparing the unmanaged control list of interfaces and the settings of features in those interfaces to the same information in a managed control you create can help reduce the chance of interoperability problems.
At some point, you'll want to perform some type of analysis on the control so that you can duplicate its interface set (not necessarily the functionality) in your own control. Getting this information means spending time in interface analysis.
Begin by highlighting the IUnknown interface. You'll see a display similar to the one shown in Figure 14-6. If you look in the right pane, you'll see that this interface has three methods. Of course, those three methods are QueryInterface() , AddRef() , and Release() . In addition, you can find the class ID of the proxy stub for this interface. Because IUnknown is a standard interface, you can be certain it will always contain all three methods-this is just part of the interface's standard package.
In many cases, you can use the information you'll find in OLE/COM Object Viewer to learn more about the way interfaces work together. For example, look at the IViewObject2 interface shown in Figure 14-7.
Notice that this interface depends on the IViewObject interface. (See the highlighted entry in the right pane.) Therefore, while the IViewObject2 interface has only 10 unique methods, it can actually provide access to 19 methods by adding the capabilities of the IViewObject interface. Notice also that this interface includes a CLSID (class ID) entry. The CLSID entry tells Microsoft Windows where to find the file that contains the interface. Files such as OLE32.DLL contain many interfaces, each of which requires a separate entry in the registry. This is the method Windows uses for keeping all facts about an interface together in one place.
The OLE/COM Object Viewer can also help you find problems with the interface support in your components. For example, if you look at the contents of the Controls That Are Safely Scriptable folder, you'll find a list of controls that have the proper interfaces and registry entries. The combination of features determines the location of the control in this case. Controls that appear in the Controls folder also appear here, so it might be helpful to see what they have that the managed control you create doesn't provide. Realize that you're searching for clues-you can accept what the OLE/COM Object Viewer gives you as absolute fact. However, it's far from certain that this utility will put you on the right track. That's where careful observation and time spent in the documentation come into play.
The Controls That Are Safely Scriptable folder might not show up in some versions of Windows. You can still check the controls for the correct interfaces.
If you take time to perform the analysis, you'll find that controls that don't normally appear in the Controls That Are Safely Scriptable folder won't include support for the IOleWindow interface. Further research will tell you that the IOleWindow interface is used to obtain window handles for in-place activation and provide context-sensitive help. Because some scripting environments require components that provide in-place activation capability, a component that doesn't implement IOleWindow isn't safely scriptable.
However, adding the IOleWindow interface would merely make the component safe for scripting-it still wouldn't place the component in the scripting folder. Windows assumes nothing about a component that you don't tell it through registry settings or through the implementation of an interface. You need to perform additional research to learn how to place the component in the scripting folder. The component also has to be marked as scriptable before it will appear in the Controls That Are Safely Scriptable folder. This is something you'd take care of once you knew all the required interfaces were in place and had tested the component fully.
You can use two methods to mark the component as safe for scripting. You can implement the IObjectSafety interface or make changes in the registry that show the component is safe. We won't go into the implementation details here; the point is that you could at least get started making your component safe for scripting by using the OLE/COM Object Viewer.