Visual C++ ATL to Java Object Example |
This example assumes you are using Visual C++ version 6.0 or later.
This example demonstrates a Visual C++ program creating an instance of a Java object, and invoking methods in that object. It also demonstrates a Visual C++ COM Component implementing a Java interface, and having a Java object invoke methods in that C++ COM component via the Java interface (without knowing that it anything other than a regular Java object).
The Java object (Main) derives from the standard Java java.util.Observable Class. This class has functionality for allowing observers (which implement the Java Interface java.util.Observable) to add and remove themselves as observers, and it has functionality for notifying Observers that the object they are observing has changed. The C++ COM Class will implement the Java java.util.Observer interface, and be added as an observer.
The Main Java object is changed every five seconds, causing the VC++ COM Object to be notified of the change every five seconds.
The steps involved
This example assumes that you have installed
the JDK under D:\JDK1.3.1
D:\jintegra
.
We will be performing this example under D:\pure\vceb
(Visual C++, Early Binding). Create that directory. Set your path
environment variable to include the JDK and J-Integra® bin
directories, and update your CLASSPATH environment to include the
J-Integra® runtime, and the current directory:
This is the Main Java class. As you can see, it registers the JVM, and then
notifies observers of the global Main instance (if it exists) every five seconds
that it has changed:
public class Main extends java.util.Observable { public static void main(String[] args) throws Exception { com.linar.jintegra.Log.logImmediately(3, "jintegra.log"); // "vcebjvm" is JVM ID specified in 'regjvmcmd'. Stands for VC++ Early Binding com.linar.jintegra.Jvm.register("vcebjvm"); // Every five seconds notify all observers that the single Main instance // has changed. int count = 1; while (true) { Thread.sleep(5000); // 5 seconds if (theMain != null) { theMain.setChanged(); System.out.println("Notifying " + theMain.countObservers() + " observers that Main has changed"); theMain.notifyObservers("Main instance has been changed " + count++ + " times."); } } } // Single instance of this class, initialised in constructor below it private static Main theMain; public Main() { theMain = this; } } |
Cut and paste the above class from your Web Browser into the Main.java
file, build it using the javac Main.java
command.
Run the J-Integra® 'java2com' tool
Start the J-Integra® 'java2com' tool using the command java com.linar.java2com.Main.
It will display a dialog, which you should fill in as follows:
This is what the settings mean:
These are the files generated by 'java2com':
Build the generated wrappers
Compile the three generated IID...Wrapper.java Java files, using the javac IID*.java command. The corresponding classes must be accessible via your CLASSPATH (which they are, because your current directory is in your CLASSPATH).
Compile the generated IDL
Use the Microsoft IDL compiler (MIDL.EXE) to compile the generated IDL
File. This will produce a type library (vcebjvm.tlb) :
Register the generated type library
Use the J-Integra® regtlb command to register the type library,
telling J-Integra® that the COM Classes defined in the type library are to
be found in the 'vcebjvm' JVM:
Register the JVM
Use the J-Integra® regjvmcmd command to register the JVM, telling
J-Integra® that the JVM with the ID 'vcebjvm' can be found on the local host,
listening on TCP/IP port 1111:
Start the Main Java program, telling the J-Integra® runtime that it should listen on TCP/IP port 1111 for DCOM requests:
Start Visual C++, and create a New Project, of type 'ATL COM AppWizard'. Change the location to 'D:\pure\vceb', and call the project 'vcobserver':
Set the Server type to EXE:
Right-click on 'vcobserver classes', and select 'New Class' from the popup:
Call the class 'Class1':
Right-click on 'Class1', and select 'Implement Interface' from the popup:
You will get a warning about there being no type library associated with the project, which you can ignore. Select 'vcebjvm generated from Main ...' from the list (if it isn't there then you have not registered the type library, as described above):
The COM class will implement the Java java.util.Observer interface:
Unfortunately the prototype for the update method that is generated in Class1 is incorrect. Replace the generated method with the following:
HRESULT __stdcall update(struct IJavaUtilObservable * p1, VARIANT p2) { static int count = 0; if(++count == 3) {// Shut this process down after three notifications _Module.ObserverShutdown(); } _ASSERTE(p2.vt == VT_BSTR); // Make sure p2 is a string _bstr_t param = p2.bstrVal; // Use COM util class to access it // Display the string that was passed MessageBox(0, (const char*)param, "VC++ Observer", MB_OK); return S_OK; } |
Add the lines marked with an arrow to the CExeModule header file:
Add the implementation of StartObserver and ObserverShutdown immediately after the existing implementation of MoniterShutdown in vcobserver.cpp:
void CExeModule::StartObserver() { |
Add a call in _tWinMain(...) in vcobserver.cpp to StartObserver() :
|
Build the project, using Build|vcobserver.exe.
Start the server, using Build|Execute vcobserver.exe. If all goes well, the VC++ observer will pop up three message boxes, and then terminate:
The Java program will display messages as it notifies the observers:
If you get this:
Set a breakpoint in the StartObserver() method, and look at the status code returned by the various method calls, and take a look at the VC errors in the Common Errors section of the Knowledge Base.
In order to run the JVM on a UNIX machine, you will need to copy across the J-Integra® runtime (jintegra.jar), the Main.class file, and the generated wrappers (IID...Wrapper.class). Start the JVM using exactly the same command as above.
On the Windows client, unregister the JVM using the command regjvmcmd/unregister
vcebjvm
, and then re-register it, passing the host name of
the UNIX box in the binding: regjvmcmd vcebjvm unixbox.host.name[1111]
.