Visual C++ ATL to Java Object Example

Java/J2EE COM Interoperability Products Page

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

  1. Configure your environment
  2. Compile the Main Java class
  3. Run the 'java2com' tool, compile the wrappers and the generated IDL, register the generated type library and the JVM, and then start the Main Java program
  4. Build and Run the VC++ COM Client
  5. Optionally, run the JVM on a remote UNIX machine


Configure your environment

This example assumes that you have installed

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:



Compile the Main Java class

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 'java2com' tool, compile the wrappers and the generated IDL, register the generated type library and the JVM, and then start the Main Java program

  1. 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':

  2. 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).

  3. 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) :

  4. 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:

  5. 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:

  6. Start the Main Java program, telling the J-Integra® runtime that it should listen on TCP/IP port 1111 for DCOM requests:


Build and Run the VC++ COM Client

  1. 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':

  2. Set the Server type to EXE:

  3. Right-click on 'vcobserver classes', and select 'New Class' from the popup:

  4. Call the class 'Class1':

  5. Right-click on 'Class1', and select 'Implement Interface' from the popup:

  6. 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):

  7. The COM class will implement the Java java.util.Observer interface:

  8. 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;
    }

  9. Add the lines marked with an arrow to the CExeModule header file:

  10. Add the implementation of StartObserver and ObserverShutdown immediately after the existing implementation of MoniterShutdown in vcobserver.cpp:

    void CExeModule::StartObserver() {
    // Create an instance of the Java Main object
    HRESULT hRes = CoCreateInstance(CLSID_main, 0, CLSCTX_ALL, IID_IMain, (void**)&m_pIMain);
    _ASSERTE(SUCCEEDED(hRes));

    // Create an instance of the VC++ Observer, accessed via the JavaUtilObserver interface
    hRes = CoCreateInstance(CLSID_Class1, 0, CLSCTX_ALL, IID_JavaUtilObserver, (void**)&m_pClass1);
    _ASSERTE(SUCCEEDED(hRes));

    // Tell the Observable object (Main) about the observer
    hRes = m_pIMain->addObserver(m_pClass1);
    _ASSERTE(SUCCEEDED(hRes));
    }
    void CExeModule::ObserverShutdown() {
    // Tell the Observable object (Main) to remove the observer
    HRESULT hRes = m_pIMain->deleteObserver(m_pClass1);
    _ASSERTE(SUCCEEDED(hRes));
    m_pClass1->Release();// No longer need the Observer m_pIMain->Release();// No longer need the Observable

    Unlock();
    }

  11. Add a call in _tWinMain(...) in vcobserver.cpp to StartObserver() :

  12. Build the project, using Build|vcobserver.exe.

  13. 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.



Optionally, run the JVM on a remote UNIX machine

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].