If you are looking for a Java-COM bridge that works for any
Java VM on any platform (yes! including UNIX), please check out
the Linar's J/Integra product.
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:
HTTP Proxies configured by IE's internet options panel. This
currently has two limitations:
Proxies that require login are not handled
Auto-proxy settings do not work
both of these problems are expected to be fixed in the next VM after
3182.
HTTPS is handled automatically - no need to buy SSL software
The IE cache is used transparently
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.
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.
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.
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.