Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

Mixing ActiveX with Java


July, 2004: Mixing ActiveX With Java

Mimicking Microsoft's JVM interface

Al is DDJ's Java newsletter editor. He can be contacted at alwal-williams.com.


Robots and Servos


With the demise of Microsoft's JVM, Java programmers can't easily access ActiveX objects. Or can they? Although the Microsoft JVM is no more, the JACOB ("Java COm Bridge") open-source library essentially duplicates its ability to allow Java code running under Windows to connect with ActiveX objects.

Many specialized libraries are only available as ActiveX (or COM) objects, so using them from Java is an important trick to have in your toolkit. I recently encountered this problem when trying to integrate a custom hardware board with a Java program. The custom board's interface expected to talk to an ActiveX DLL. Rather than rewrite the library in Java, I decided to interface the existing library to my Java code.

There are several commercial solutions available, but I selected JACOB (written by Dan Adler and available at http://danadler.com/jacob/) because it closely mimics the Microsoft JVM's interface. This is both a strength and a weakness. The Microsoft JVM has plenty of documentation, so there are lots of examples and resources. The weakness is that the Microsoft JVM is disappearing, and JACOB depends on Microsoft documents for its primary documentation. However, there is an active user's group for people using JACOB, and the necessary files are available from the group.

If you are a Java purist, you've probably already stopped reading. After all, mixing anything with Java—and Windows-specific code, in particular—is sure to flare tempers. However, politics aside, it is often necessary to interoperate with other software. Like it or not, there is a large body of ActiveX code out there and being able to call it from Java simply expands the number of projects you can do in Java instead of using some other language.

Using JACOB

To show how JACOB works, I wanted to control the robot in Figure 1 from a PC running a Java program. Admittedly, this robot isn't a commercial project, but I use it to demonstrate a board that controls servo motors from a PC. Like a lot of commercial devices, the robot's wheels are servo motors, and the board provides an ActiveX DLL that lets you control up to eight motors. (For more information, see the accompanying text box entitled "Robots and Servos.")

Of course, to be practical, the robot would need to carry a Java-enabled PC (maybe a PC/104 board) or have a wireless serial connection. However, for my purposes here, I simply kept the robot tethered to a long RS-232 cable. My goal was to build a command-line program that could issue instructions to the robot's drive motors. You could then use the program in a batch file or even add the class to a JavaScript Interpreter (see my article about hardware control with JavaScript in "Low-Level I/O Control with Java" in Dr. Dobb's Java Programming E-Zine, http://www.ddj.com/downloads/).

Understanding ActiveX

If you've worked at all with JavaBeans, you won't find ActiveX mysterious. At the lowest level, an ActiveX object is simply any piece of code that exposes one or more interfaces. These interfaces are tables of function pointers. So an object might publish an interface where the fifth function in the table causes the object to, say, generate a report.

By itself, this provides encapsulation, but little else. The secret to ActiveX is that each interface contains at least three pointers (the first three, obviously) that perform the same function. In particular, these three functions make up the IUnknown interface.

This shows one form of ActiveX polymorphism. By ignoring all but the first three functions of an interface, you can treat any interface as an IUnknown interface (just as you can treat any Java object as type Object).

However, there is another, more common form of polymorphism used by ActiveX. One of the functions defined by IUnknown is QueryInterface. So suppose you are building a database for your local brick and mortar library. You have an object that exposes an imaginary IMedia interface. Because IMedia is a superset of IUnknown, you can call QueryInterface to discover if the object also has an IBook interface. If it does, then it must be a type of book. If it doesn't, then it isn't a book (perhaps it is a CD with an ICDRom interface). So by exploiting QueryInterface, you can treat a CD or a book as a type of media, which is, of course, polymorphism.

This is ActiveX at the core level. If you are a C programmer, there isn't much more to it than this. There are many predefined interfaces you can provide (or use), but they all assume that you know which slot in the interface table has the function you want to use.

For programming languages such as Visual Basic (a major user of ActiveX technology), this is too great of a restriction. Foreknowledge of the interface table amounts to early binding—the language tool has to know about the object you wish to access. So how can ActiveX perform late binding (where the runtime system resolves the function reference)?

IDispatch

The answer is through a specialized interface, IDispatch. This is a special interface that lets you refer to functions in an object via name or number. Because early binding is more efficient, some objects provide custom interfaces in addition to IDispatch. In fact, some objects provide dual interfaces that are IDispatch interface tables followed by custom functions. After all, programs expecting IDispatch will just ignore the extra functions.

IDispatch provides for three main items: properties, methods, and events. As you might expect, properties are quasi-variables, while methods are simply function calls. An event is a way to register a function with an object. The object can then use the function to communicate with the original caller. For example, a button object might call a programmer-defined function when users press a button.

ActiveX also supports many common data types. However, most variables used by IDispatch-implementing objects will be variants. This is very similar to an untyped Visual Basic variable. It can contain nearly anything (numbers, strings, dates, currency, object references, and even arrays).

Most ActiveX objects of interest will provide IDispatch and work through various properties, methods, and events. JACOB allows you to very easily access these parts of an ActiveX object. If the object doesn't support IDispatch, you won't be able to use JACOB to access it.

Using JACOB

JACOB uses a special class to represent an IDispatch interface. The constructor for this class takes a string that is usually the ActiveX server's progid. This is simply a short name that identifies the server (for example, Microsoft Excel's progid is Excel.Application). If you want to use the classid (a 128-bit number that is unique for each server), you can provide it using an alternate syntax.

There are several ways you can handle properties, methods, and events in JACOB (although events are handled a bit differently than in the original Microsoft JVM). In addition, JACOB provides classes to represent variants and other specialized ActiveX types.

For example, consider accessing data from Excel, as in Example 1. You can also call static members of Dispatch to achieve the same effect. When you call a method, you must provide an array of Variant objects that represent the arguments. Example 2, for instance, is a call to the robot's servo controller board.

There are also helper methods of the Dispatch class that take a varying number of arguments, if you prefer to use them. Example 3(a), for instance, is essentially the same as writing Example 3(b). You can find other syntax examples by reading the JACOB source or referring to the Microsoft documentation.

A Java Wrapper

The robot's servo controller board (a GP4; http://www.awce.com/gp4.htm) has an ActiveX object that provides several methods and a property to set the active COM port. Obviously, it would be possible to just write the robot controller program to use the ActiveX object directly via JACOB.

However, I will eventually write an actual Java object that interfaces with the board. With that in mind, I wrote a wrapper around the ActiveX object that simply provides the same methods and properties. Eventually, I'll replace this class with one that is pure Java and the remaining code will not require any changes.

Listing One is the result. The constructor creates the ActiveX object (the progid is AWCGP4DLL.GP4DLL). Each method corresponds to a method or property in the original object. For variety, I commented out the invoke call in the setMask method and replaced it with a Dispatch object method that does the same function.

Armed with this wrapper class, it is easy to use it to control the motors. Listing Two is a command-line program that does the job. The program accepts one or two speed arguments (which can range from -50 to 50). If you provide one argument, the motors move the robot forward (or backward if you use a negative number) with a speed proportional to the magnitude of the argument. With two arguments, you can set the speed and direction of each wheel independently. In either case, the program runs the motors for about three seconds and then stops them.

If you read the command-line program's source code, you'll notice that the code has no idea that the servo manipulation is being handled by ActiveX. All the JACOB code is restricted to the GP4 class in Listing One.

Pros and Cons

I could have rewritten the servo controller's library using javax.comm and handled it in native Java. I have no doubt that would be a better solution. After all, with the ActiveX component, the program only runs under Windows. With javax.comm, the program would operate with Linux, Macintosh, or Windows.

However, rewriting the module would take time—time to rewrite it and also time to test it. In this case, I had the luxury of having the source code to the ActiveX components, but if it were from a third party, it might be even more difficult to reproduce. What's more is that changes from that third party could be difficult to incorporate.

With a JACOB wrapper, it is fast and easy to incorporate the ActiveX code into a Windows-only Java program. Even if the ActiveX object added features later, it would be simple to either ignore them or add them to the wrapper with a minimum of effort.

Politics aside, there are times when you need to absorb some ActiveX code. The unfortunate legal wrangling has made that more difficult than it used to be. But thanks to JACOB, it's only a little more difficult.

DDJ



Listing One

/* Java wrapper for GP-4 ActiveX DLL. Requires:
   JACOB -- http://danadler.com/jacob/
   GP4 -- http://www.awce.com/gp4.htm
*/
import com.jacob.com.*;
import com.jacob.activeX.*;
public class GP4
{
    private ActiveXComponent servo;  // The COM object
    // Create ActiveX object
    public GP4() 
    { 
        servo=new ActiveXComponent("AWCGP4DLL.GP4DLL");
    }
    // Reset servo controller
    public void reset() 
    { 
        servo.invoke("Reset",new Variant[] {} );
    }
    // Set servo position
    public void setPosition(int chan, int pos) 
    {
        servo.invoke("SetPosition", 
              new Variant [] { new Variant(chan), new Variant(pos) });
    }
    // Enable channel
    public void enableChannel(int chan, boolean enable) 
    {
        servo.invoke("EnableChannel", 
            new Variant[] { new Variant(chan), new Variant(enable) });
    }
    // Turn groups of servos on/off
    public void setMask(int mask)
    {
        // Another way to do this
        // servo.invoke("SetMask", new Variant [] { new Variant(mask) });
        Dispatch.call(servo,"SetMask",new Variant(mask));
    }
    // Enable/disable all servos
    public void enable(boolean enflag)
    {
        servo.invoke("Enable", new Variant [] { new Variant(enflag) });
    }
    // Set the COM port
    public void setComPort(int port)
    {
        servo.setProperty("Comport",new Variant(port));
    }
}
Back to article


Listing Two
/* The robot driver */
public class ServoDrive
{
    // General-purpose pause
    {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {}
    }
    // Convert a string into a speed number
    private static int setSpeed(String v,int defspeed)
    {
      int speed;
      try {
          speed=Integer.parseInt(v);  // read integer
      }
      catch (Exception e) { speed=0; }
      if (speed==0) speed=defspeed;   // or use default
    return speed;
    }
    // The main code
  public static void main(String[] args)
  {
      GP4 servos=new GP4(); // create a servo controller
      int servoA=7;         // the wheels are on servo #7 and #6
      int servoB=6;
      // read speeds
      int speedA=setSpeed(args.length>0?args[0]:"",25);
      int speedB=setSpeed(args.length>1?args[1]:"",-speedA);
      // set the COM port
      servos.setComPort(1);
      // Turn servos on
      servos.enable(true);
      // Set them to run
      servos.setPosition(servoA,speedA);
      servos.setPosition(servoB,speedB);
      pause(3000);  // 3 seconds
     // disable all servos so they stop at once
      servos.setMask(0);
      // Reset both servos
      servos.setPosition(servoA,0);
      servos.setPosition(servoB,0);
      // And reenable them (although they are stopped now
      servos.setMask(0xFF);
 }
}
Back to article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.