Programmer's Guide



Chapter 32   Using COM Objects

This chapter describes SilverStream's COM support. It gives an overview of COM integration with SilverStream and explains how to call COM services directly from SilverStream applications.

This chapter covers the following topics:

About COM integration   Top of page

In some situations, a SilverStream application needs to be able to call native code. SilverStream applications can use the Java Native Interface (JNI) to call native methods. However, JNI is a low-level interface that requires that you write additional C or C++ code. Writing good JNI code is difficult.To simplify access to native code, SilverStream lets you use either of the following programming techniques:

The remainder of this chapter describes how to invoke the services of COM objects. For information about making native calls to DLLs, see Accessing DLLs Directly.

COM Basics   Top of page

COM is Microsoft's Component Object Model. COM is a specification that describes how executable components can interoperate in a seamless manner. COM objects are language-independent. They can be implemented in any language that can produce a binary layout that conforms to the COM standard.

The remainder of this section provides a very brief overview of COM. For complete details on COM, see your Microsoft documentation.

COM objects

The COM architecture builds on object-oriented concepts:

Since COM is object-oriented, it maps easily to the object-oriented concepts inherent in Java.

COM and the Windows registry (GUIDs, CLSIDs, IIDs, and ProgIDs)

COM uses the Windows Registry to keep track of information about COM components registered on your system. To identify components, COM uses GUIDs (Globally Unique Identifiers), generated numbers that are guaranteed to be unique at any time and on any computer. A GUID can identify a COM class (in which case it's known as a CLSID) or a COM interface (known as an IID). The COM programmer can also specify a ProgID for a component. A ProgID is a friendly string identifier for a component.

All information about COM components is stored in the HKEY_CLASSES_ROOT branch in the Windows Registry.

Type Libraries

The services provided by a COM component are described in a type library. A type library is a binary file that identifies the supported interfaces and method signatures. The type library for a component can be a separate .TLB file, or a resource that is incorporated into the .DLL or .EXE file.

HRESULTs

COM functions return a value of the type HRESULT. The HRESULT data type reports errors in a standard format. Since a COM function must return an HRESULT, COM methods cannot use the return value to return data back to the caller. Instead, COM methods must use an [out, retval] parameter to return results.

SilverStream support for COM   Top of page

SilverStream allows the Java programmer to access COM nonvisual objects. It does not provide support for visual controls. In addition, SilverStream does not support events or callbacks; SilverStream classes can invoke the services of COM components, but COM components cannot call back to SilverStream classes.

COM support is built into the Microsoft Virtual Machine. However, because SilverStream 3.0 uses the Sun Virtual Machine, which does not have built-in COM support, SilverStream provides a proprietary mechanism for COM integration. SilverStream's support for COM is very similar to that provided by the Microsoft Virtual Machine.

SilverStream provides a SilverCmd utility called ComGen that exposes COM objects to Java. When you run ComGen against a type library, it generates a set of Java source files that correspond to the classes and interfaces defined in the type library. You can then compile these Java files and use them in your SilverStream code to access one or more COM objects.

ComGen compared with JActiveX

ComGen performs many of the same operations as jactivex, a Microsoft tool that reads a type library and generates a set of Java source files that represent the contents of the type library. However, unlike jactivex, ComGen is not tied directly to the Microsoft virtual machine and compiler. This gives the SilverStream developer some flexibility, but also introduces some restrictions. Among the restrictions is the fact that ComGen does not provide support for visual controls. Note that SilverStream COM integration is intended for use on the server; if you use COM objects on a Java form, you need to ensure that the objects are available on all clients that will run the form.

What you need to do to use a COM object in SilverStream

To access a COM object from a SilverStream application, you need to:

  1. Identify the type library that contains the COM object you want to use.

    To do this, you can run ComGen with the -l switch to generate a list of the type libraries registered on your system. Alternatively, you can use the OLE/COM Object Viewer provided by Microsoft.

  2. Run ComGen on the type library to generate a set of Java source files that represent the contents of the type library.

  3. Compile the generated Java source files.

  4. Create a JAR file that contains the Java classes.

  5. Upload the JAR file to the SilverStream Server.

  6. Add the JAR file to any business object, form, or page that needs to be able to access the COM object.

  7. Write Java code in SilverStream to call one or more functions associated with the COM object.

Working with ComGen   Top of page

ComGen exposes COM objects to Java. When you run ComGen against a type library, it generates a set of Java source files that correspond to the classes and interfaces defined in the type library. You can then compile these Java files and use them in your SilverStream code to access one or more COM objects.

For each CoClass in a type library, ComGen generates a Java class that extends a base class called AgCoClass. This base class does not provide any public methods or fields that you need to access in your code. It implements base class functionality used internally by SilverStream.

For each Interface in a type library, ComGen generates a Java class that extends AgCoInterface. The only public methods associated with this interface that you need to use in your code are equals() and release().

Example 1: Listing available type libraries   Top of page

The following example shows how you can run ComGen with the -l switch to identify the type libraries that are available on your system, as shown below:

  silvercmd comgen -l 

This command produces output that looks like this:

  Microsoft ActiveX Data Objects Recordset 1.5 Library 
-> C:\Program Files\Common Files\system\ado\msador15.dll
OLE Automation
-> C:\WINNT\System32\stdole32.tlb
Visual Basic For Applications
-> C:\Program Files\Common Files\Microsoft Shared\VBA\VBA332.dll
Visual Basic For Applications
-> C:\WINNT\System32\VBAEN32.OLB
Microsoft Graph 8.0 Object Library
-> C:\Program Files\Microsoft Office\Office\GRAPH8.OLB
Microsoft Excel 5.0 Object Library
-> C:\Program Files\Microsoft Office\Office\XL5EN32.OLB
...

Example 2: Generating Java source files for a type library   Top of page

The following command generates Java source files for EventLogger.dll, the library for the KeyTech Windows EventLogger ActiveX control. The -d switch specifies the root directory for the output and the -p switch specifies the target package. ComGen writes all .java files to c:\test\evtlog:

  silvercmd comgen -d c:\test -p evtlog c:\EventLogger\EventLogger.dll 

The resulting Java classes are created in the evtlog package.

NOTE   You can download the KeyTech Windows EventLogger ActiveX control from http://www.keytech.com.au/download.html.

Writing Java code to access a COM object   Top of page

After you use ComGen to generate the Java source files for a COM object, you need to compile the resulting source files, make a JAR file that contains the compiled classes, upload the JAR to the SilverStream Server, and then add the JAR to any business object, form, or page. Once you've completed these steps, you're ready to write Java code in SilverStream to access the COM object.

NOTE   If you use COM objects on a form, you need to ensure that the COM objects in question are available on all clients that will run the form.

Coding basics   Top of page

To access a COM object from a business object, form, or page, you need to:

  1. Add the JAR file to the business object, form, or page.

  2. Add an import statement to import the package that contains the COM class.

  3. Add an import statement to import the com.sssw.rt.com package.

  4. Instantiate the COM class and cast it to a COM interface by calling the castFrom() method on the interface.

  5. Call a function on the COM interface.

Exception handling

Ordinarily, a COM client uses the HRESULT returned by a function to check for errors. This is not the case in the Java context. Classes generated by ComGen detect HRESULTs that signal errors and throw a ComException. To handle COM exceptions in SilverStream code, you can wrap a COM function call in a try block and specify ComException as the data type in the exception handler. If the function has a parameter marked as [retval], this parameter is returned by the Java function.

Reference counting

COM uses reference counting to manage the lifecycle of an object. The object is kept in memory as long as the number of clients using the interfaces implemented by this object is greater than zero. When the reference count reaches zero, the object is removed from memory. This rule applies to the SilverStream implementation of the COM bridge. Therefore, every instance of an object representing a COM interface must be released before the actual COM object can be destroyed. During Java garbage collection, every interface is automatically released. However, to exercise more control over when COM objects are destroyed, you can call the release() function on the COM interface. Calling release() invalidates a COM object. If you call a function on a COM interface after calling release(), a RuntimeException is thrown.

Data types

Most of the data types used by COM objects map easily to Java data types. When you run ComGen against a type library, it automatically generates code that uses appropriate Java data types. To allow you to work with Variants, SilverStream provides a Variant class. The Variant class acts as a wrapper around the VARIANT data type in COM.

Example 1: Making a simple function call   Top of page

The following code demonstrates the use of a COM object inside a SilverStream function. It uses several methods that are defined in EventLogger.dll, the type library for the KeyTech Windows EventLogger ActiveX control. To see the contents of this type library (including the IDL for each method), you can use the OLE/COM Object Viewer provided by Microsoft.

NOTE   You can download the KeyTech Windows EventLogger ActiveX control from http://www.keytech.com.au/download.html.

The SilverStream function readOldestRecord() performs these operations:

  1. Creates the EventLog object representing the COM class and casts it to the IEventLog interface by calling the castFrom() method.

  2. Calls the getOldestRecordNumber() function on the IEventLog interface. The IDL for the corresponding COM method is shown below:

      HRESULT OldestRecordNumber([out, retval] long* pVal); 
  3. Calls the ReadRecord() function, which returns an IEventLogRecord interface. The IDL for the corresponding COM method is shown below:

      HRESULT ReadRecord( 
       [in] long RecordNumber,
       [out, retval] IEventLogRecord** pRecord);
  4. Calls the getCategoryDescription() function on the IEventLogRecord interface. The call to getCategoryDescription() is wrapped in a try block. The IDL for the corresponding COM method is shown below:

      HRESULT CategoryDescription([out, retval] BSTR* pVal); 
  5. Returns a string that includes the category description returned by getCategoryDescription(), along with the event description returned by a call to getEventDescription(). The IDL for the EventDescription COM method is shown below:

      HRESULT EventDescription([out, retval] BSTR* pVal); 

Here's the code for the readOldestRecord() function:

  public String readOldestRecord() { 
   //create an object and cast it to an interface
   IEventLog eLog=IEventLog.castFrom(new EventLog());
   //perform operation on that interface
   int recNumber = eLog.getOldestRecordNumber();
   //get another interface
   IEventLogRecord logRec=eLog.ReadRecord(recNumber);
   String category = null;
   try {
      category = logRec.getCategoryDescription();
   } catch (ComException ex) {
      category = "none"; //handle COM errors
   }
   return "Oldest =" + recNumber + ", category =" +
      category + ", " + logRec.getEventDescription();
}

Example 2: Using the Variant class   Top of page

This example demonstrates the use of the SilverStream Variant class. In addition, it shows how to force a COM interface to be released. This example builds on Example 1, and therefore also uses methods that are defined in EventLogger.dll.

The SilverStream function dumpStringInfo() performs these operations:

  1. Creates the EventSource object representing the COM class and casts it to the IEventSource interface by calling the castFrom() method.

  2. Calls the putSourceName() function on the IEventSource interface. The IDL for the corresponding COM method is shown below:

      HRESULT SourceName([in] BSTR pVal); 
  3. Creates two Variant objects (vtStr and vtDat).

  4. Calls the ReportInformation() function on the IEventSource interface, passing in the two Variant objects as parameters. The IDL for the corresponding COM method is shown below:

      HRESULT ReportInformation( 
       [in] long EventId,
       [in, optional] VARIANT Strings,
       [in, optional] VARIANT Data);
  5. Releases the IEventSource interface by calling the release() function.

Here's the code for the dumpStringInfo() function:

  public void dumpStringInfo(String info) { 
   //create an object and cast it to an interface
   IEventSource evtSrc = IEventSource.castFrom(new EventSource());
   evtSrc.putSourceName("ComGen");
   //create and populate two variants
   Variant vtStr = new Variant();
   vtStr.putString (new Date() + ": " + info);
   Variant vtDat = new Variant();
   vtDat.putNull();
   //pass the variants in as parameters
   evtSrc.ReportInformation (666, vtStr, vtDat);
   //force the release of the interface
   evtSrc.release();
}

To test Example 1 and Example 2 together, you could add code to a SilverStream page that calls the readOldestRecord() and dumpStringInfo() functions:

  String desc = readOldestRecord(); 
dumpStringInfo(desc);

In the Event Viewer, you would see the following output when you ran the page:

If you were to click on the event, you would see this detail information about the event:

Migrating from JActiveX to ComGen   Top of page

The procedure for migrating SilverStream 2.x code that relies on JActiveX to SilverStream 3.0 code that uses ComGen is relatively straightforward. The basic structure of the application does not need to change. To migrate from JActiveX to ComGen, you need to regenerate the supporting Java classes for a COM object and make a few modifications to the Java code that uses the object.

For each COM object you want to use in SilverStream, you need to:

  1. Run ComGen to replace the set of Java source files generated by JActiveX.

  2. Compile the generated Java source files.

  3. Create a JAR file that contains the Java classes.

  4. Upload the JAR file to the SilverStream Server.

  5. Add the JAR file to any business object, form, or page that needs to be able to access the COM object.

For each SilverStream business object, form, or page that uses the COM object, you need to:

  1. Replace the import com.ms.com.* statement with import com.sssw.rt.com.*.

  2. Remove any other references to com.ms.* from your code.

  3. Replace cast operators with calls to the castFrom() method on the interface.

    For example, suppose you had the following line of code:

      IEventSource evtSrc = (IEventSource) new EventSource(); 

    In this case, the cast operator (IEventSource) would be replaced with a call to the castFrom () method on the IEventSource interface.

      IEventSource evtSrc = IEventSource.castFrom(new EventSource()); 
  4. In each exception handler, replace ComFailException with ComException.

  5. Replace ComLib.release() calls with calls to the release() method on the COM interface.






Copyright © 2000, SilverStream Software, Inc. All rights reserved.