Click Here to Install Silverlight*
United StatesChange|All Microsoft Sites
MSDN
|Developer Centers|Library|Downloads|Code Center|Subscriptions|MSDN Worldwide
Search for


Advanced Search
MSDN Home > MSJ > April 1997
April 1997


George Shepherd develops mapping software for Orbital Software and delivers training courses for DevelopMentor. Scot Wingo is cofounder of Stingray Software, producing MFC extension class libraries. George and Scot wrote MFC Internals (Addison-Wesley, 1996).

Q I hear about Java everywhere I turn! I want to look into it even though I've committed myself to the COM lifestyle. What's the relationship between Java and COM, and is there any way I can start using Java to implement my COM objects?

A Hold on to your coffee cup and please continue to live the COM lifestyle! As it turns out, you can use Visual J++ to implement COM objects. This month, we'll look at the relationship between COM and Visual J++.

COM 101 COM is the plumbing that underlies Microsoft's ActiveX technologies. As a binary object model, COM allows developers to write objects independently (for example, at different times and using different languages) and have all the objects work together seamlessly at runtime. This is no small feat, and COM lays out a number of rules to make sure the integration can take place. Let's take a moment to examine the most important aspects of COM before seeing how Visual J++ handles it. These include COM interfaces, COM classes, COM class objects, and COM servers.
At the heart of COM is the notion of an interface. In fact, the most important point to understand about COM is that it's a formal separation of interfaces and implementations. In COM, the interface comes first. To that end, Interface Definition Language (IDL) is the official language for defining COM interfaces. IDL uses a C-like syntax to describe interfaces unambiguously. For the time being, COM defines a close cousin to IDL called Object Description Language (ODL). You can use both languages to describe COM interfaces. While IDL is the official language for defining interfaces, ODL is adequate for our purpose of implementing a COM object with Java.
COM interfaces by themselves merely represent contracts with the client code about how the functions are called (the function signatures, for example). COM interfaces are implemented by COM classes. COM classes are bodies of code that implement at least one COM interface. All COM classes implement reference counting (for lifetime control of the object) and a way to navigate from one interface to another. This functionality is described by an interface named IUnknown, which is implemented by every COM class. IUnknown has three functions: QueryInterface for navigating through interfaces, and AddRef and Release to handle reference counting. Furthermore, this functionality is exposed through every COM interface, which simply means that the first three functions of every COM interface you ever see will be QueryInterface, AddRef, and Release.
COM classes live within COM servers—either DLLs or EXEs. COM classes implemented within DLLs share the same process space as their clients. COM objects implemented within EXEs live in different process spaces than their clients.
The final piece to the COM puzzle is something called a class object. Class objects are a metaclass for COM classes. For example, if a COM server has five COM classes inside, that COM server will also have five class objects—one for each kind of COM class within the server. COM class objects usually implement an interface named IClassFactory2. Clients ultimately use IClassFactory2 to instantiate COM objects. Instead of using language-dependent instantiation mechanisms (like operator new in C++), COM clients use well known operating system functions to instantiate COM objects.
That's COM in a nutshell. For a more exhaustive treatment of the subject be sure to check out Inside OLE by Kraig Brockschmidt (Microsoft Press, 1995) or George Shepherd and Scot Wingo's MFC Internals (Addison-Wesley, 1996).

Doing COM Classes in Java Let's dive right into writing a COM class using Java. Imagine that you want to implement some COM classes representing three-dimensional objects (like a sphere and a cylinder, for example). Also imagine that these three-dimensional objects expose interfaces to determine the volumes and surface areas of the three-dimensional objects. We'll define interfaces for getting the volume of a three-dimensional object, the surface area of a three-dimensional object, and interfaces representing a sphere and a cylinder. Figure 1 shows the ODL code defining the interfaces and COM classes.
Notice that, following the rules of COM, each interface and each class is uniquely identified by a GUID. Also notice that each interface derives from IUnknown (so that QueryInterface, AddRef, and Release are the first three functions in every interface). The ODL simply describes the interfaces and two COM classes that will implement the interfaces. (A side note—the IDL for describing the COM interfaces and classes would look very similar to the ODL code.)
This is the point at which Visual J++ begins to differ from C++ for writing COM classes. If we were using C++ to implement the COM classes, the ODL or IDL would be optional; in C++, COM interfaces are defined using pure abstract base classes, and you can cook up some pure abstract base classes without the help of ODL or IDL. The advantage to using ODL is that the by-products of compiling the ODL code include a C++ header file (useful for implementing the COM class in C++ and publishing for clients) and a type library (necessary for implementing the COM class in Visual J++). If you define the interfaces in IDL, you get the added benefit of a proxy-stub DLL (useful for remoting the interface).
Once the ODL code is written, you simply need to add the file to your Visual J++ project and set up a custom build step for it. Figure 2 shows the custom build settings for the ODL file.

Figure 2 Custom build settings for the ODL file
Figure 2 Custom build settings for the ODL file

MKTYPLIB.EXE is the type library compiler. The /nocpp option tells MKTYPLIB to not use the C preprocessor (which isn't available with Visual J++). The /h geoshapes.h tells MKTYPLIB to emit a header file named geoshapes.h. This header file will include the actual C++ pure abstract base class that clients can use to converse with the COM object. The output of MKTYPLIB is a type library named geoshapes.tlb. We'll need the type library to implement the COM object in Java.
If you describe the interfaces in IDL, you'll want to use MIDL.EXE in the custom build setup. If you use the correct command-line options, the MIDL compiler will produce the type library and C++ header as well. Refer to the MIDL compiler documentation for the appropriate command-line settings.

Creating the Java Binding Recall that COM interfaces are described using pure abstract base classes in C++. Pure abstract base classes end up representing function tables once they're compiled. Writing COM classes in C++ involves populating the function table so clients can call the functions. Of course, this implies that native COM classes and interfaces are based on pointers. But wait a minute—Java doesn't support pointers! How can you implement a COM class in Java? The answer lies in the type information produced by compiling the ODL or IDL file.
Visual J++ includes a tool named JavaTLB that will read the type library and write the bytecode files (the Java .CLASS files) based on the interfaces described in the type library. The command-line syntax for JavaTLB is:

JavaTLB SomeTypeLib.TLB
For example, running JavaTLB on the GeoShapes type library yields six Java .CLASS files for the COM classes and their interfaces: CoCylinder.class, CoSphere.class, ICylinder.class, ISphere.class, ISurfaceArea.class, and IVolume.class. JavaTLB puts these in the trusted library directory.
Once the .CLASS files have been generated, it's simply a matter of typing up the Java code to implement the COM classes. Figure 3 shows the Java code for implementing the COM classes.
In Figure 3, the first two lines import the packages necessary to get the COM class working. The first import statement for com.ms.com pulls in the Visual J++ COM support. Notice that the Java classes do not implement the IUnknown functions; writing COM classes in C++ requires you to implement the IUnknown functions by hand. One great benefit to using Visual J++ to implement COM classes is that IUnknown is handled for you! The second import statement pulls in the Java bindings (the .CLASS files generated by JavaTLB).
The rest of the class is fairly straightforward Java code. Notice that both CoCylinder and CoSphere use Java's mechanism for describing multiple interfaces on a single class. Also notice that while the native COM interfaces return HRESULTs (a requirement for going out-of-proc), the Java functions return void and throw ComExceptions to indicate errors. The last thing to notice about the Java classes is how they return the results of the calculations. Because the COM interface functions return HRESULTs, the calculation results must be passed back out as parameters. Though Java doesn't support reference parameters, you can get by that limitation by treating out parameters as arrays of size one.
So there it is—a COM class implemented in Java. It's fairly straightforward if you can get used to the language syntax and the notion of defining your interfaces in ODL or IDL. There's just one thing left. Where does the Java-based COM object live?

The Server Traditional C++-based COM classes usually live inside a DLL or EXE concocted by the developer of the class. These C++-based COM servers also contain class objects and are registered in the system registry. That way, the Service Control Manager (SCM) knows where to go when it needs to fire up a COM server requested by a client. But remember, we're dealing with Java, where everything exists in the form of bytecodes. When using Java, there's no opportunity for writing a DLL or EXE server to house a COM object. This leads to the next question: what entry goes into the registry to make the Java-based COM object accessible? The answer is that the Microsoft Java Virtual Machine DLL (the Java VM) is registered as the inproc server for the COM class. Here's how it works.
As with any other COM class, the sphere and cylinder classes are named by GUIDs. Were you to write the sphere and cylinder classes in C++ and put them in a DLL, you would register that DLL in the registry. That way, the SCM can use the registry to find the path to the DLL implementing the class.
If the COM class is implemented in Java, then the Java VM is registered in the registry. Specifically, the registry entries for CoSphere look like this:

HKEY_CLASSES_ROOT\
    CLSID\
        {98765436-ABCD-EFEF-6543-00A686867B22}\
            InprocServer32 = msjava.dll
                JavaClass = CoSphere
Listing an InprocServer32 key under the GUID for CoSphere is standard, but the value named JavaClass is new.
When a client calls CoGetClassObject for the CoSphere object, the SCM sees MSJAVA.DLL as the inproc server. The SCM loads the Java VM and makes an entry into DllGetClassObject. The Java VM provides an implementation of IClassFactory so clients can create the COM object, and it knows which class to create because CoSphere corresponds to the JavaClass value under the InprocServer32 key. All this works because the CoSphere symbol and the GUID for CoSphere were related back in the original ODL or IDL file like so:
[ uuid(98765436-ABCD-EFEF-6543-00A686867B22)]
coclass CoSphere {
    interface ISphere;
    interface IVolume;
    interface ISurfaceArea;
}
You may choose to add these registry settings by hand, but we don't recommend it. Alternatively, Microsoft includes a utility named JavaReg that will make the entries for you. The JavaReg command-line syntax for the CoSphere class looks something like this:
javareg /register /class:CoSphere /clsid:{98765436-ABCD-EFEF-6543-00A686867B22}
The /register command-line switch tells JavaReg to register the class. The /class switch indicates the name of the Java class. Finally, the /clsid switch indicates the GUID representing the COM class.
Once the COM class is registered, it'll function just like any other COM object provided the .CLASS files generated by the Java compiler are accessible. That means you could even write a C++ client to exercise the Java-based COM class, and the C++ client will never know the class was implemented in Java.

Using COM Objects from Java But what about the other side? Can you use COM objects from within Java source code? Absolutely. In fact, writing COM client code in Java is easier than normal because you don't need to worry about the sticky AddRef/Release rules that you do when writing COM client code in C++. Let's take a quick look at what it takes to use a COM object from Java.
As with implementing COM objects in Java, using COM objects within Java requires type information. JavaTLB comes to the rescue again, providing Java bindings for the COM classes and interfaces. Once the type information is processed into .CLASS files, Java's import statement will make the bindings available:

import geoshapes.*
Once the Java program understands the COM object by reading the type information, you can use new to instantiate the COM class just as you would any other Java class:
ICylinder cylinder;
cylinder = (ICylinder)new CoCylinder();
The Java VM calls CoCreateInstance to load the COM object and retrieve the desired interface. Notice that the new CoCylinder is cast to the ICylinder interface. This is because CoCylinder is a COM object and can't be referred to directly (all communication happens through interfaces in COM). Calling COM interface functions is just like calling regular Java class functions:
cylinder.SetDims(100.00, 10.00);
Performing QueryInterface in Java is simply a matter of casting from one interface to another. The Java VM traps this and calls the native QueryInterface.
IVolume volume = (IVolume)cylinder;
double d[] = new double[1];

volume.Volume(d);
The Java VM handles all the work of mapping Java calls to the COM interface and managing calls to QueryInterface. As a developer writing COM client code, you never have to deal with those annoyances!
Finally, notice that AddRef and Release are never called in Java. Java doesn't need to use AddRef and Release explicitly because it is garbage-collected; Java is very helpful at taking care of one of COM's most error-prone areas.

Conclusion As it turns out, it's fairly easy to implement a COM class using Java. While as a language Java remains pointer challenged, describing COM interfaces using ODL or IDL and generating a type library is sufficient for creating a binding for Java. At that point, creating a Java-based COM class is a matter of importing the bindings and writing the Java code to implement the interfaces. There's no reason to fuss with the IUnknown functions—the Java VM already handles the interface negotiation and reference counting (yipee!). As an added bonus, the JavaVM handles IDispatch (for Automation) and COM aggregation (for implementing binary reuse).
Our example illustrates what it takes to implement two COM objects within Java. Overall, Java is probably one of the better languages for implementing COM classes because it's less error-prone; it's missing most of the dangerous C++ features like pointers. However, if you're a C++ developer taking a look at the Visual J++ support for COM, take it from us: the Java VM is doing much of the work you're doing by hand if you're using C++.

Have a question about programming in Visual Basic, Visual FoxPro, Microsoft Access, Office or stuff like that? Sent it to Scot Wingo at visual_developer@stingsoft.com or to George Shepherd at 70023.1000@compuserve.com.

From the April 1997 issue of Microsoft Systems Journal. Get it at your local newsstand, or better yet, subscribe.

© 1997 Microsoft Corporation. All rights reserved. Legal Notices.

© 2017 Microsoft Corporation. All rights reserved. Contact Us |Terms of Use |Trademarks |Privacy & Cookies
Microsoft