by Al Saganich
Last month, in the article "The Basics of ActiveX" we discussed how to load and use ActiveX controls. We also briefly explored the limitations of ActiveX, the largest of which is the lack of support for hosting visual ActiveX controls. This lack of support is a real drawback, and many developers have shied away from using ActiveX as a result. While many interesting and useful non-visual ActiveX controls exist, the most interesting have some visual aspect.
Well, here’s some good news: This limitation is no more! With the release of the final version of SDK 2.0 for Java, Microsoft has provided a new set of classes specifically targeted at hosting visual ActiveX controls. While I’d planned to discuss several other aspects of ActiveX controls, the new com.ms.activeX package is so interesting that I just had to experiment with it.
Visual ActiveX and JavaJava applications can now host visual ActiveX controls via the com.ms.activeX package of classes, chief of which is the ActiveXControl class. As a result, you can develop ActiveX-enabled Java applications completely within Java. Prior to this new ActiveX package, a developer who wanted to use an ActiveX control was limited to non-visual controls if that application had to be completely Java. Or, you could load the control directly into your Web page, then pass it into your Java application. There were several problems with this approach: As I mentioned, most of the interesting controls have a visual aspect (some sort of window), and you needed a browser that supported the <OBJECT> tag to load them.
Internet Explorer 3.02 was the first browser to support embedded ActiveX objects natively. A Netscape plug-in exists for ActiveX controls—but you’re still forced to load the control and pass it to your applet, making standalone Java applications impossible. The new ActiveX classes solve these problems.
Prepping the controlYou need to complete several steps before you can use an ActiveX control from Java. You must register the control using Regsvr32.exe, then run JActive.exe to create the .java files that correspond to the control(s) interfaces. Finally, you must run the Java compiler on the generated files to generate the .class files Visual J++ requires.
This process diverges slightly from the way we used ActiveX controls via the Java Type Library tool in the past. Basically, the JActiveX tool does everything the Java Type Library did and more. (See the SDK-20 documentation for a complete list of the JActiveX tool parameters.) Let’s look at these preparation steps in more detail.
Register the controlFirst, run Regsvr32.exe to register a new ActiveX control. Microsoft controls and most third-party controls register themselves when you install them, so you must run regsvr32 only if you’re registering a new control. Regsvr32.exe ships with Visual J++, Visual C++, and Windows NT—basically, any environment in which you might want to develop your own ActiveX controls. A sample Regsvr32.exe command might look like the following:
c:\> regsvr32.exe mycontrol.ocx
Generate the source
Second, run the JActiveX.exe tool to generate the source Java files. In previous incarnations of Visual J++ and ActiveX, you could run the Java Type Library wizard on a registered control. However, visual controls are more sophisticated, and Microsoft has improved JavaTLB into JActiveX. The new incarnation supports all the previous features and adds new ones. A sample JActiveX.exe command might be as follows:
c:\> JActiveX /javatlb mycontrol.ocx /l
mycontrol.lst
Listing A shows a portion of the Calendar.java file generated by JActiveX.exe.
Listing A: Calendar control-generated .java file
// Auto-generated using JActiveX.EXE 4.79.2228
// (e:\sdk-java.20\bin\JActiveX /javatlb
// c:\winnt\system32\mscal.ocx /l calendar.lst)
//
// WARNING: Don’t remove the comments that include
// "@com" directives. This source file must be
// compiled by a @com-aware compiler. If you’re using
// the Microsoft Visual J++ compiler, you must use
// version 1.02.3920 or later. Previous versions
// won’t issue an error but won’t generate COM-
// enabled class files.
package mscal;
import com.ms.com.*;
import com.ms.com.IUnknown;
import com.ms.com.Variant;
import stdole2.*;
/** @com.class(classid=
8E27C92B-1264-101C-8A2F-040224009C02,
DynamicCasts)
*/
public class Calendar implements IUnknown,
com.ms.com.NoAutoScripting, ICalendar
{
/** @com.method() */
public native int getBackColor();
/** @com.method() */
public native void putBackColor(int pclrBackColor);
/** @com.method() */
public native short getDay();
/** @com.method() */
public native void putDay(short pnDay);
/** @com.method() */
public native stdole2.Font getDayFont();
/** @com.method() */
public native void putDayFont(
stdole2.Font ppfontDayFont);
/** @com.method() */
public native int getDayFontColor();
/** @com.method() */
public native void putDayFontColor(
int pclrDayFontColor);
/** @com.method() */
public native short getDayLength();
/** @com.method() */
public native void putDayLength(short pnDayLength);
. . .
/** @com.method() */
public native boolean getShowDateSelectors();
/** @com.method() */
public native void putShowDateSelectors(
boolean pfShowDateSelectors);
. . .
public static final com.ms.com._Guid clsid =
new com.ms.com._Guid((int)0x8e27c92b,
(short)0x1264, (short)0x101c, (byte)0x8a,
(byte)0x2f, (byte)0x4, (byte)0x2, (byte)0x24,
(byte)0x0, (byte)0x9c, (byte)0x2);
}
Compile the code
The final step in preparing a visual control for use is to compile the generated code. If you look closely at the JActiveX command, you’ll see that I specified the /l parameter. This parameter generates a list of files that need to be compiled into .class files, which are then loaded at runtime. You’ll use the /l parameter mostly for convenience—but it keeps errors to a minimum, so I highly recommend it. Compile the generated sources with a command similar to
c:\> jvc @mycontrol.lst
Now, let’s get to work and create a control.
Note: Commands, commands, where are my commands?
I assume that the regsvr32, JActiveX, and jvc commands are in your path somewhere. For example, on my Windows NT machine, e:\sdk-java.20\bin is in my path. As a result, the operating system can find the JActiveX.exe and JVC.exe commands. You may or may not need to specify the complete path to these commands.
Getting startedFirst things first: Before you can use an object in Java, you must create it. ActiveX objects are no different. You need to import the correct packages, then new an instance of the ActiveX object. In this respect, ActiveX objects are a little different from Java objects. An ActiveX object is a single object that may have one or more interfaces; each interface defines a different way of referencing the object.
For example, a single object may both produce and consume input. Such an object might have two interfaces: one for output (produce) and one for input (consume). Last month we built and used an ActiveX control that functioned just this way (see "The Basics of ActiveX"). This month, we’ll experiment with Microsoft’s standard Calendar control.
As we discussed last month, you create an instance of the control—in this case, Calendar—but cast it into a variable that corresponds to the interface you wish to use for the control. Most ActiveX controls have a default interface, and, for the most part, you could get away with using the object that you new directly. However, if you always cast the control to the desired interface, you’ll never have to wonder if the default supports the methods you require.
ActiveX controls differ from Java classes in one significant way: They have properties that define how the controls are displayed when they’re created. Listing B shows a sample application that creates and uses a Calendar control. The next few sections examine this code in detail; we’ve highlighted the important lines in color.
Note: Some things have been removed.
We’ve omitted parts of the code that have nothing to do with ActiveX.
Listing B: ActiveXTest.java
// ActiveXTest.java
// 11-November-1997
// Al Saganich for Visual J++ Developer’s Journal
//
// Copyright, 1997, Al Saganich,
// Visual J++ Developer’s Journal.
// All rights reserved.
//
// This code is provided without warranty of any
// kind and may or may not be fit for any purpose
// other than illustration. This code may be used,
// changed, or distributed in any form, electronic or
// otherwise, as long as this notice is not removed.
import java.awt.*;
import java.awt.event.*;
import com.ms.ui.*;
import com.ms.activeX.*;
import com.ms.com.ConnectionPointCookie;
// for support classes for the calendar control.
import stdole2.*;
// For Calendar control itself.
import mscal.*;
public class ActiveXTest
extends
Frame
implements
DCalendarEvents,
ActiveXControlListener,
ItemListener
{
//Interface we want to access the control via
ICalendar c; <
// ActiveX control host
ActiveXControl host;
// event connection object
ConnectionPointCookie eventconnector;
// I like testing with JView.exe!
static public void main(String args[])
{
ActiveXTest frame = new ActiveXTest();
. . .
}
/*
* adapter class to handle window close events.
*/
class ShutdownAdapter extends WindowAdapter
{
. . .
}
// DCalendar interface methods required to catch
// events. Note that for illustration all we do
// is print that an event was signaled.
public void NewYear(){
System.out.println("Newyear called");};
. . .
public void KeyDown(short a[],short b)
{System.out.println(
"KeyDown method called");};
// Item listener interface methods
public void itemStateChanged(ItemEvent ie)
{
Object obj = ie.getItem();
// Is it our check box?
if (obj instanceof String &&
"Show Date Selector".
equals((String)obj) )
{
if (ie.getStateChange() ==
ItemEvent.SELECTED)
c.putShowDateSelectors(true);
else
c.putShowDateSelectors(false);
c.Refresh();
}
}
// We implement the ActiveXControlListener
// controlCreated interface so we’ll know when
// the visual representation of the object has
// been created.
public void controlCreated(Object obj)
{
// Paranoid but check anyway
if ( obj instanceof ActiveXControl )
{
// What did we get?
com.ms.com.IUnknown what =
((ActiveXControl)obj).getObject();
// Again paranoid.
if (what instanceof ICalendar)
{
c.Today();
c.putShowDateSelectors(false);
c.Refresh();
}
}
}
public ActiveXTest()
{
super ("ActiveX Control Test");
. . .
// Now deal with the control.
try
{
c = (ICalendar) new Calendar();
}
catch (Exception e)
{
System.out.println(
"Exception on new Calendar() = " + e);
}
try
{
eventconnector = new
ConnectionPointCookie(c, this,
Class.forName(
"mscal.DCalendarEvents"));
}
catch (Exception e)
{
System.out.println(
"Exception on ConnectionPointCookie "
+ e);
System.out.println(
"Cannot catch DCalendarEvents events!");
}
// create the host
host = new ActiveXControl(c);
host.addActiveXControlListener(this);
add("Center",host);
}
}
Imports and such
Every Java application has a set of imports that defines the set of classes the applet or class needs. In our example, we want to use the Microsoft Calendar control. The actual file that represents this control resides (under Windows NT, anyway) in c:\winnt\system32\mscal.ocx. When compiled, it produces the package mscal, which we import with the statement
import mscal.*;
The calendar control also uses a group of standard OLE2 types that are all part of the stdole2 package. If you’ve never used JActiveX on a control, you’ll probably see this package generated when you generate the code for the calendar control itself. So, we import the stdole2 package as follows:
import stdole2.*;
Last month we examined in detail how to create an ActiveX control from within a Java application—and surprise, we do it the same way this month! Since we need to generate a Calendar control and we want to use its ICalendar interface (interfaces are often—but not always—prefixed with an I) our new statement looks something like this:
ICalendar c = (ICalendar)new Calendar();
Up to this point we haven’t used any of the new com.ms.activeX package classes. In fact, there’s another way to create a control: We could have created it directly via the ActiveXControl object’s constructor. However, if you do so, you won’t have a way to access the control that’s actually created (as you’ll see in a moment, you can get a handle to the control itself and not just the ActiveX host object). Creating a control this way has one big drawback: It’s hard to call methods. For diehards who want to see it all, just look in the Microsoft documentation that bundles with Java SDK 2.0 under the ActiveX package.
After we’ve created a control, we create its ActiveX host normally and pass the control into the ActiveXControl constructor, as follows:
ActiveXControl host = new ActiveXControl(c);
Herein lies the main benefit of using an ActiveXControl: One of its parent classes is Canvas. And you can insert Canvas objects into frames and panels and the like, allowing visual controls to be directly inserted into Java applications.
A second benefit of using the ActiveXControl object is that it supports a special event listener, the ActiveXControlListener interface. This interface defines a single method, controlCreated, which is called when a control has done an addNotify call. What addNotify does isn’t important. What is important is that the object has been created completely and can be manipulated at this point. Let’s see this process in action.
We define the class as implementing the ActiveXControlListener interface, as follows:
public class ActiveXTest
extends
Frame
implements
DCalendarEvents,
ActiveXControlListener,
ItemListener
We then implement the required method:
public void controlCreated(Object obj)
{
// Paranoid but check anyway
if ( obj instanceof ActiveXControl )
{
// What did we get?
com.ms.com.IUnknown what =
((ActiveXControl)obj).getObject();
// Again paranoid.
if (what instanceof ICalendar)
{
c.Today();
c.putShowDateSelectors(false);
c.Refresh();
}
}
}
This method checks to see whether it was called with an ActiveXControl. I’m not sure what else you could call it with, but it’s better to be safe than sorry!
We then check that our object was created. (After all, there’s nothing stopping you from creating multiple ActiveX objects.) Finally, we set two of the control’s properties. The first is the day with which to seed the calendar; Today() seems logical. Next, we turn off the date-selector fields within the control to prevent the user from changing the date. The last step refreshes the control.
Properties and property bagsActiveX controls aren’t quite Java classes. They have one feature that Java classes lack: properties. Properties are like variables within a control that you can set and get via accessor methods. Normally, if a control has a foo property, you can set it with the setProperty method of the ActiveXControl object. This method takes a string as its first argument and is overloaded to take a variety of other types as its second argument. If your foo property took an integer, you could set it with the statement
host.setProperty("foo",3);
The JActiveX tool can also wrap properties in get, set, and put methods, so it’s really your choice which to use.
Once the control has been created and initialized, we create its host control—based on the actual control—with a statement like
host = new ActiveXControl(c);
We then add the control’s listener with the line
host.addActiveXControlListener(this);
Then, we add the control to the panel that will contain it
add("Center",host);
and we’re almost finished. Figure A shows the control within a frame.
Figure A
Our Calendar control looks like this with the date selectors disabled.
Events and methodsWe aren’t quite done. I’ve left out one important step that I covered last month, but which is important and bears revisiting—events and methods. Let’s touch on the basics.
Most controls send events and have methods. The Calendar control defines an event interface, DCalendarEvents, which defines a set of methods that are called when appropriate. We capture these events by implementing the interface, then attaching the interface to the object with a ConnectionPointCookie. The code will look something like
eventconnector = new ConnectionPointCookie(
c, this,
Class.forName(
"mscal.DCalendarEvents"));
This month’s example links events and methods via a simple check box that manages the capabilities of the control. One of the things the Calendar control can do is display or hide its date-selector GUI elements. I added a check box to the bottom of my panel, then trapped events on this check box to set display of the selector fields. The important code is in the ItemStateChanged method; it turns date selection on or off. The following code snippet shows the calendar control methods in color:
if (ie.getStateChange() == ItemEvent.SELECTED)
c.putShowDateSelectors(true);
else
c.putShowDateSelectors(false);
c.Refresh();
Figure B shows the result of enabling the date selection fields. As you can see, calling ActiveX control methods looks exactly like calling Java class methods!
Figure B
We’ve now enabled the Calendar control’s date selectors.
ConclusionMicrosoft filled a large hole in ActiveX when it added the new ActiveX hosting package. Developers can now take full advantage of ActiveX controls completely from within Java. In addition, all aspects of visual control—properties, methods, and events—are supported natively. Developers will no longer need kludgy methods, such as combining the HTLM <OBJECT> tag and Java, to perform a single task.
Al Saganich is an independent software consultant, currently under contract to Digital Equipment Corporation. He's co-author of the best-selling Microsoft Visual J++ 1.1 SOURCEBOOK and is working on the next release of Java 1.2 for C/C++ Programmers, both published by J. Wiley and Sons. You can reach Al at asaganich@aol.com.
Copyright © 1998, ZD
Inc. All rights reserved. ZD Journals and the ZD Journals logo are trademarks of ZD
Inc. Reproduction in whole or in part in any form or medium without
express written permission of ZD Inc. is prohibited. All other product
names and logos are trademarks or registered trademarks of their
respective owners.