Simple COM objects support at least one interface; very complex COM components, such as embeddable OLE objects, support several interfaces. Each interface describes a different contract between server and client; the number of interfaces you define depends on the number of faces your server shows to the system.
Every COM interface is a subclass of IUnknown, which is implemented internally by the JVM. As you learned in Chapter 5, an interface inherits three methods from IUnknown: QueryInterface, AddRef, and Release; these methods provide the core functionality of COM. Java hides many of the details of COM programming; for example, you will almost never call the IUnknown methods directly. But you should nevertheless be aware of what those functions do and why they exist, particularly when you are developing your own COM classes in Java. I’ll discuss QueryInterface here; AddRef and Release will be discussed later in this chapter, in the section about reference counting.
As mentioned above, Java communicates with COM objects only through interfaces. When you create a COM object, you don’t access it directly, as you would a Java object; instead, you use one of the object’s defined interfaces. Through IUnknown’s QueryInterface method (known as QI to many developers), you can request any interface supported by a COM component. Since all COM interfaces derive from IUnknown, you can reach any supported interface through any other by calling QI.
In C++, you must use the correct IID to request an interface pointer through QI. Java, however, simplifies the use of QI immensely; in fact, your Java code will probably never call QueryInterface directly, nor is it likely to use a single IID. Instead, Java allows you to cast one interface pointer to another, while it handles the details of calling QI with IIDs. The following code shows how this works.
// Obtain a pointer to an interface
IMath util = (IMath)new CUtilityCOM();
// Now cast that pointer to a new interface type
IUseStates ius = (IUseStates)util;
When you base an interface on IUnknown, all calls to the interface’s methods occur through pointers that are stored in a virtual table that the operating system initializes with references when the object is loaded. Although Java and C++ include support for tables of function pointers, languages such as Microsoft Visual Basic, Scripting Edition, do not. To make COM objects accessible without pointers, COM provides an additional standard interface, IDispatch, which provides a further level of indirection. IDispatch, which derives from IUnknown, adds a method named Invoke. Instead of calling an interface method directly via a pointer, a Visual Basic, Scripting Edition, program calls IDispatch::Invoke, which in turn looks up the pointer required for calling the desired function. If an interface supports both pointer-based and indirect calls to its methods, it is known as a dual interface.
The use of IDispatch and IUnknown is transparent in Java programs. In many cases, COM objects support only the indirect method invocation of IDispatch because this facility can be used by any programming language. However, when you are designing your own components, consider implementing a dual interface; this will allow languages such as Java to use the most efficient technique by directly calling methods through their pointers.