Dan Adler's Java-COM Resources

http://danadler.com
dan@danadler.com

Read the Site Review


General

If you are looking to integrate Java with COM you have several options:



RMI / JDBC

My article in Java Report on Using RMI and JDBC From Visual Basic can be found here in MS-Word format.

The most frequently asked question is how to use RMI with the Microsoft VM. You need to either use the RMI.zip file which is available here, or, you can simple put the JDK 1.1.* classes in your CLASSPATH. I use the latter method.

Since I wrote the paper, I made some key advances in the area of RMI/COM:

Generic RMI Client Class

In the paper I erroneously claimed that you need to write a Java client for each new RMI server. All you have to do is wrap java.rmi.Naming.lookup in a class and cast the return type to a java.rmi.Remote. Then, you can use this from VB for any RMI server without writing any new Java code. A generic class of this type may be found here. The VB code that uses this would look like this:

  Set x = GetObject("java:RmiLookup")
  Set y = x.getInstance("localhost", "LookupServer")

then, y is late bound to the remote instance. If you want to bind to a new RMI service, just change the arguments to getInstance with no new Java code.

RMI Moniker

If you can write a generic Java/COM bridge to RMI, why not make it even more COM-friendly, and register it as a Moniker? The nice thing about that, is that RMI then becomes a natural extension to COM, and VB programmers don't even need to know there is any Java involved. The VB code would then look like this:

  Set y = GetObject("rmi://localhost/LookupServer")

All you have to do to enable this amazing magic is to write the "rmi" Moniker in Java. Then, simply javareg it under the progid "rmi", and let COM and the Java VM do all the rest. Here is the code for RmiMoniker. The exact command to register the class is:
javareg /register /class:RmiMoniker /progid:rmi

JDBC Moniker

You can easily modify the RMI Moniker code to create a JDBC Moniker, which, given a valid JDBC url, will return a java.sql.Connection object. Once you have that, you can talk to UNIX databases from VB/VBA/VBScript without writing any Java code.

Enterprise Java Beans

It's also trivial to modify the RmiMoniker to act as an EJB Moniker that binds to the javax.ejb.EJBHome interface by using a JNDI lookup instead of Naming.lookup in RmiMoniker. Once you have that, the MS Java VM will do all the subsequent bridging for you, so you can effectively write client-side code that talks to a UNIX-based EJB server directly from VB/VBA/VBScript without writing any Java code.

The RMI/COM Hybrid Programming Model

It's clear to me that adding RMI, EJB and JDBC to the standard set of tools available to VB/VBA/VBScript programmers offers a very powerful model for communicating with enterprise UNIX servers, which is not available in any other way.

On the other hand, it's also clear to me that neither Sun nor Microsoft are very likely to push this model. Sun, because they are still hoping that all client-side code will be written in pure Java, and Microsoft, because they are still hoping that all server-side code will be written in pure COM.



Serializable Variant

The com.ms.com.Variant class is a wrapper to the C++ VARIANT type. The great thing about VARIANTs is that they are automatically marshalled by value. This applies to base types as well as SAFEARRAY's and lately even UDT's. If a Variant were also Serializable, then you could use it over RMI.

I hope to post an example of this soon. But the key step is having a java.io.Serializable Variant. Microsoft did not make it easy. The code for marshalling VARIANTs is not documented anywhere, and the com.ms.com.Variant class is final (cannot be extended). I got around both of these hurdles and the result is the class: MVariant.



Using WinInet

Using WinInet with URLConnection

The WinInet library has been wrapped by the Microsoft VM team as a Stream handler. This means that with one line of code, you can turn Java's URLConnection class into one that supports:

This example demonstrates how to turn on WinInet and fall back to the standard Java behavior when using another VM.

There is one caveat with this. You have to set the Stream Handler before creating the first instance of java.net.URL, otherwise it will silently not work. The reason is the silly way the stream handlers get initialized by the factories. This means you have to include this line of code as one of the first things you do in your program.

Using WinInet Through J/Direct

If you want 100% control over all aspects of downloading, your best bet is to use WinInet function calls directly. This sample demonstrates how to call most of the WinInet functions from Java. This example essentially does the same as the URLConnection one. I spent some time mapping all the constants from the various include files, so this is a pretty functional little class. It also shows how to use the status callback to get a trace on what is actually happening.


Using the Url Moniker to Download

Somewhere between the ease-of-use of URLConnection and the fine-grained control of WinInet lies the Url Moniker. I've created wrappers that allow you to use it from Java. You can find it all in urlmonsamp.zip. If you don't want to deal with the bind status callback or the http negotiate interfaces, then you really don't need these wrappers, and just use the J/Direct calls in UrlDownload.java passing a null for the bind parameter.
  1. I isolated the minimal IDL into urlmon.idl (included in the zip file) to include only IBindStatusCallback and IHttpNegotiate (and all of the baggage that comes with that). I then ran that through MIDL and Jactivex to obtain the JCW's.
  2. The file UrlDownload has two methods. One to download any URL into a cache file, and the second to open it to a stream. The latter is not that different from using URLConnection, except that it allows you to get better OnProgress notifications (and cancel) if you are using a progress monitor GUI for the download, or if you use it in asynch mode.
  3. You can see the spots (commented out) where a progress monitor GUI can be inserted. This works both for synchronous as well as asynchronous download. In the synchronous case, the progress monitor would have to run in a separate thread. If the progress GUI has a Cancel button, then the IBindStatusCallback::OnProgress needs to return E_ABORT, which is implemented in Java by throwing an exception. The IHttpNegotiate interface can be used to manipulate the HTTP headers before the request is sent, and to deal with cases where the HTTP response code was not 200 (e.g. some kind of navigation error occured). All of this is implemented in bindingStatus.java in the zip file.

To run it you can use: jview UrlDownload <url> .



Browsing to a URL

How do you open the default browser to a URL? This example show how to use the HlinkNavigateString function from urlmon.dll to accomplish this through J/Direct. For some reason this fails on Windows 95...


Browsing to a Folder

How do you ask the user to pick a directory? This nifty little wrapper uses the Win32 call from shell32.dll to ask the user to pick a directory and print it out.


Creating Automation Components

In some cases, you may decide (as I often do) not to use Jactivex'd wrappers, and to use the com.ms.com.Dispatch class directly to invoke methods and access properties. In the past, I tended to use:
ActiveXComponent xl = new ActiveXComponent("Excel.Application");
This works ok in some cases, but fails miserably in others. The reasons are discussed at length in the Java-COM list. The preferred way is to explicitly create and release the component using code like this. This allows the component to be created on the current thread. Once you create an object like this, you can use the Dispatch class to manipulate it, and then release it when you're done.


Copying an AWT Image to the Clipboard

Java's clipboard handling is pretty awful. You can copy/paste text and in Java 2 you can do custom formats, but what about the formats that Windows apps support? In particular, if you have an AWT component on the screen (say, a chart) and the user wants to copy and paste the image into a Word doc, what do they do? The ClipPaster class solves this problem by using a back door in Microsoft's AWT implementation (courtesy of Tracy Sharpe...) and creating a metafile from the image by replacing the default Graphics class passed into printAll with one that is created on a metafile instead of a window.

This class implements IDataObject with a major shortcut. Instead of messing with FORMATETC and all its "beauty" in getData, it delegates that to the WFC Metafile class. The other ugliness is the use of the thread, which is necessary because AWT is locked while OLE wants to keep talking, so without the separate message-pump thread the whole thing freezes.



Creating an MD5 Hash String

This isn't really COM-related, but it's cool and useful. What if you have various text string of varying lengths, and you want to create fixed size hash strings out of them? How do you make the hash strings be unique and yet map back faithfully? The answer is to use the MD5Hash class.