Search code examples
javac++swigjna

Wrap a DLL into Java


I've got some code to talk to a hardware device on windows which is working in C++. The code does something pretty simple to react to a button push on the device and I have this compiled into a dll with an observer that is called when the button is pushed. I now need to interface this with a big Java program.

I was intending to use JNA but it only works with C and I cannot see how to do this with an Observer pattern in C. I've looked into using BridJ and SWIG (both of which cliam to work on C++ DLLs) to create an interface to the compiled dll (with the associated header file) but BridJ creates huge amounts of files (in JNAeratorStudio) and then stops with an error and I cannot see how to get started on Windows with SWIG (I'm using Visual Studio Express rather than full Visual Studio).

Does anyone know of a tutorial on integrating a C++ DLL with a Java Program - SWIG looks pretty promising but the tutorials are 'swampy'.

I've put some simple C code to talk to the DLL below:

#include <iostream>
#include <stdio.h>

#include "DeepFocusControlDll.h"

using namespace std;
using namespace DeepFocusControl;

class MyObserver : public DeepFocusControl::DeepFocusObserver{
    void Event(){
        printf("***Button Pushed***");
    }
};

int main()
{
    DeepFocusControl::AVA6Control* dfc = new DeepFocusControl::AVA6Control();
    MyObserver* observer = new MyObserver();
    dfc->AddObserver(observer);
    bool connected = dfc->IsConnected();
    printf("Connected %s\n", (connected)?"true":"false");
    bool connectresult = dfc->Connect();
    printf("Connecting %s\n", (connectresult)?"true":"false");
    connected = dfc->IsConnected();
    printf("Connected %s\n", (connected)?"true":"false");
    dfc->SetHardwareAppLaunch(false);
    char waitChar;
    cin >> waitChar;
    dfc->SetHardwareAppLaunch(true);
    dfc->RemoveObserver(observer);
    dfc->Disconnect();
    printf("Disconnected\n");
    cin >> waitChar;
}

If anyone knows a simpler way to use an observer pattern on this I can happily recode the C side too.


Solution

  • It sounds like you're looking for SWIG's directors feature. In its simplest form you can use directors with SWIG by giving an interface file like:

    %module(directors=1) MyModule
    %feature("director");         
    
    %{
    #include "mydll.h"
    %}
    
    %include "mydll.h"
    

    Given a header file "mydll.h":

    class Observer {
    public:
      virtual void frobination() = 0;
      virtual ~Observer() {}
    };
    
    inline void bar(Observer *o) {
      o->frobination();
    }
    

    Then you can run SWIG:

    swig -Wall -java -c++ mymodule.i
    

    This will generate three Java classes: MyModule, MyModuleJNI and Observer. Of these MyModule will contain all the free functions from your header file, exposed as static member functions since Java has no such thing as free functions. You can safely ignore MyModuleJNI - it's glue generated by SWIG for connecting MyModule to the real C++ implementations. You'll need to compile mymodule_wrap.cxx for MyModuleJNI (and hence MyModule) to work correctly though and load the DLL using System.loadLibrary before you call any functions from them.

    The Observer class directly corresponds to the Observer interface in mydll.h. You should derive from it in Java and override the frobinate function to give it your own implementation:

    public class Test extends Observer {
      @Override
      public void frobination() {
        System.out.println("go go gadget frobinator");
      }
    
      public static void main(String[] argv) {
        System.loadLibrary("mymodule");
        Test t = new Test();
        MyModule.bar(t);    
      }
    }
    

    Which I can compile and run to do exactly what you'd hope.

    If you want you can automate the call to System.loadLibrary by adding:

    %pragma(java) jniclasscode=%{
      static {
        try {
            System.loadLibrary("mymodule");
        } catch (UnsatisfiedLinkError e) {
          System.err.println("Native code library failed to load. \n" + e);
          System.exit(1);
        }
      }
    %}
    

    to your SWIG interface file.

    If your real header file is that simple it should be that simple to get the same results too. If it's more complicated you can instruct SWIG to special case some of its wrapping in various ways.