F. de Coligny and S. Griffon - 1 July 2015
This doc shows how to build a C/C++ dll (dynamic link library) with functions which can be called from Java through JNA, under Windows 64 bits.
Note: using this procedure with a 32 bits Windows or compiler would restrict the use of the resulting dll with a 32 bits Java, resulting in a limitation of memory available for the programs to less than 2 Gb, which may be an uncomfortable limitation → 64 bits highly recommended, for programs with a much higher memory available
Reference: https://www.mingw.org/wiki/sampledll
Java version: java version “1.7.0_67” Java(TM) SE Runtime Environment (build 1.7.0_67-b01) Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode)
Windows 7 Professionnal 64 bits (6.1, version 7601)
Dell Latitude E6510, Intel core I7
mingw is a c/c++ compiler for windows (Minimalist GNU for Windows, a GCC compiler port for Windows)
This source code example proposes few C functions to illustrate how the parameters may be passed and returned through JNA (see the JNA section below).
simpleDLL.h
extern "C" { int __declspec(dllexport) multiply (float a, float b, float & result); int __declspec(dllexport) sumArray (float a [], int length, float & result); char * __declspec(dllexport) getVersion(); int __declspec(dllexport) addValue (float a [], int length, float value); }
simpleDLL.cpp
#include "simpleDLL.h" #include <stdexcept> using namespace std; int multiply (float a, float b, float & result) { result = a * b; return 0; } int sumArray (float a [], int length, float & result) { result = 0; for(int i=0; i<length; i++) { result += a[i]; } return 0; } char * getVersion() { return "1.2.3"; } int addValue (float a [], int length, float value) { for (int i = 0; i < length; i++) { a[i] += value; } return 0; }
The commands below must be typed (or pasted / adapted) in the Mingw terminal (see upper section). In this terminal, change directory to reach the directory where you have your C source files.
1. Compile the source code
D:\coligny\workspace\capsis4\ext\windows64>g++ -c -m64 simpleDLL.cpp
2. Link stage, with dll generation
D:\coligny\workspace\capsis4\ext\windows64>g++ -shared -m64 -o simpleDLL.dll sim pleDLL.o
The result is a dll called simpleDLL.dll in the same directory.
Dependency Walker explores a dll and tells which modules / functions are inside, with their accurate names. It can check if the dll is 32 or 64 bits (what we want is 64) and detect other problems in the dll.
Source: How to tell if .dll is 32 bits or 64 bits?
Here is an example of Java class to wrap the dll with the JNA technology. Some types are transformed with the JNA features.
In this example, pure Java methods are provided, to be called transparently from other classes. They point to the C functions in the dlls.
A main method calls the methods to illustrate.
Important note: the directory containing the dll must be in the jna.library.path variable, you may use this parameter while launching the java program:
-Djna.library.path=“%path_to_dll%”
package capsis.lib.phelib; import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.ptr.FloatByReference; /** * SimpleDll: a link to the simpleDLL.dll dynamic link library. * * @author F. de Coligny, S. Griffon - July 2015 */ public class SimpleDllTest { // This is the standard, stable way of mapping, which supports extensive // customization and mapping of Java to native types. public interface DllInterface extends Library { DllInterface INSTANCE = (DllInterface) Native.loadLibrary("simpleDLL", DllInterface.class); int multiply(float a, float b, FloatByReference result); int sumArray(float[] a, int length, FloatByReference result); String getVersion (); int addValue (float a [], int length, float value); } public float multiply(float a, float b) { FloatByReference r = new FloatByReference (0); int rc = DllInterface.INSTANCE.multiply (a, b, r); return r.getValue (); } public float sumArray(float[] a) { FloatByReference r = new FloatByReference (0); int l = a.length; int rc = DllInterface.INSTANCE.sumArray (a, l, r); return r.getValue (); } public String getVersion () { String v = DllInterface.INSTANCE.getVersion (); return v; } public void addValue (float[] a, float v) { int l = a.length; DllInterface.INSTANCE.addValue (a, l, v); } /** * A test method */ public static void main(String[] args) throws Exception { SimpleDllTest t = new SimpleDllTest(); System.out.println("SimpleDll calling multiply ()..."); float r = t.multiply(3.1f, 3f); System.out.println("SimpleDll r: " + r); System.out.println("SimpleDll calling sumArray ()..."); float[] a = new float[]{1.1f, 2.2f, 3.3f}; r = t.sumArray(a); System.out.println("SimpleDll r: " + r); System.out.println("SimpleDll calling getVersion ()..."); String s = t.getVersion (); System.out.println("SimpleDll r: " + s); System.out.println("SimpleDll calling addValue ()..."); a = new float[]{1.1f, 2.2f, 3.3f}; t.addValue(a, 5.5f); String b = "a[]: "; for (int i = 0; i < a.length; i++) { b += a[i]; b+= " "; } System.out.println("SimpleDll r: " + b); } }
Compile and launch your Java program with a jdk 64 bits.
Run example:
D:\coligny\workspace\capsis4>java -cp class;ext\*;ext\windows64 -Djna.library.pa th="ext\windows64" capsis.lib.phelib.SimpleDllTest SimpleDll calling multiply ()... SimpleDll r: 9.299999 SimpleDll calling sumArray ()... SimpleDll r: 6.6000004 SimpleDll calling getVersion ()... SimpleDll r: 1.2.3 SimpleDll calling addValue ()... SimpleDll r: a[]: 6.6 7.7 8.8
Here are the changes to be made in simpleDLL.h and simpleDLL.cpp to be able to create a foo object, keep a pointer on it, and then be able to call its methods several times from Java. The example shows that the getState () returns changing values of the foo object.
// simpleDLL.h #include "foo.hpp" Foo * foo; extern "C" { int __declspec(dllexport) multiply (float a, float b, float & result); int __declspec(dllexport) sumArray (float a [], int length, float & result); char * __declspec(dllexport) getVersion(); int __declspec(dllexport) addValue (float a [], int length, float value); int __declspec(dllexport) initFoo (float initState); char * __declspec(dllexport) getFooVersion (); float __declspec(dllexport) getFooState (); } // simpleDLL.cpp #include "simpleDLL.h" #include <stdexcept> using namespace std; int multiply (float a, float b, float & result) { result = a * b; return 0; } int sumArray (float a [], int length, float & result) { result = 0; for(int i=0; i<length; i++) { result += a[i]; } return 0; } char * getVersion() { return "1.2.3"; } int addValue (float a [], int length, float value) { for (int i = 0; i < length; i++) { a[i] += value; } return 0; } // extern C functions wrapping the Foo class // They can be called from Java by JNA int initFoo (float initState) { foo = new Foo (); foo->init(initState); } char * getFooVersion () { return foo->getVersion (); } float getFooState () { return foo->getState (); }
// foo.hpp #include <string> class Foo { private: char * m_version; float m_state; public: Foo (); int init (float initState); char * getVersion (); float getState (); }; // foo.cpp #include "foo.hpp" // Constructor Foo::Foo() { } int Foo::init(float initState) { m_state = initState; } char * Foo::getVersion () { return "4.5.6"; } float Foo::getState () { m_state += 0.1; return m_state; }
C compilation and link to build the dll with foo inside:
D:\coligny\workspace\capsis4\ext\windows64>g++ -c -m64 foo.cpp D:\coligny\workspace\capsis4\ext\windows64>g++ -c -m64 simpleDLL.cpp D:\coligny\workspace\capsis4\ext\windows64>g++ -shared -m64 -o simpleDLL.dll simpleDLL.o foo.o
package capsis.lib.phelib; import com.sun.jna.Library; import com.sun.jna.Native; import com.sun.jna.ptr.FloatByReference; /** * SimpleDll: a link to the simpleDLL.dll dynamic link library. * * @author F. de Coligny, S. Griffon - July 2015 */ public class SimpleDllTest /* extends OrgTools */{ // static { // // Set jna.library.path // System.setProperty("jna.library.path", System.getProperty("java.library.path")); // } // This is the standard, stable way of mapping, which supports extensive // customization and mapping of Java to native types. public interface DllInterface extends Library { DllInterface INSTANCE = (DllInterface) Native.loadLibrary("simpleDLL", DllInterface.class); int multiply(float a, float b, FloatByReference result); int sumArray(float[] a, int length, FloatByReference result); String getVersion (); int addValue (float a [], int length, float value); // Using the foo object int initFoo (float initState); String getFooVersion (); float getFooState (); } public float multiply(float a, float b) { FloatByReference r = new FloatByReference (0); int rc = DllInterface.INSTANCE.multiply (a, b, r); return r.getValue (); } public float sumArray(float[] a) { FloatByReference r = new FloatByReference (0); int l = a.length; int rc = DllInterface.INSTANCE.sumArray (a, l, r); return r.getValue (); } public String getVersion () { String v = DllInterface.INSTANCE.getVersion (); return v; } public void addValue (float[] a, float v) { int l = a.length; DllInterface.INSTANCE.addValue (a, l, v); } // Using the foo object public int initFoo (float initState) { DllInterface.INSTANCE.initFoo (initState); return 0; } public String getFooVersion () { return DllInterface.INSTANCE.getFooVersion (); } public float getFooState () { return DllInterface.INSTANCE.getFooState (); } /** * A test method */ public static void main(String[] args) throws Exception { SimpleDllTest t = new SimpleDllTest(); System.out.println("SimpleDll calling multiply ()..."); float r = t.multiply(3.1f, 3f); System.out.println("SimpleDll r: " + r); System.out.println("SimpleDll calling sumArray ()..."); float[] a = new float[]{1.1f, 2.2f, 3.3f}; r = t.sumArray(a); System.out.println("SimpleDll r: " + r); System.out.println("SimpleDll calling getVersion ()..."); String s = t.getVersion (); System.out.println("SimpleDll r: " + s); System.out.println("SimpleDll calling addValue ()..."); a = new float[]{1.1f, 2.2f, 3.3f}; t.addValue(a, 5.5f); String b = "a[]: "; for (int i = 0; i < a.length; i++) { b += a[i]; b+= " "; } System.out.println("SimpleDll r: " + b); System.out.println("SimpleDll calling foo methods..."); t.initFoo (1f); System.out.println("SimpleDll fooVersion: "+t.getFooVersion ()); System.out.println("SimpleDll fooState: "+t.getFooState ()); System.out.println("SimpleDll fooState: "+t.getFooState ()); System.out.println("SimpleDll fooState: "+t.getFooState ()); System.out.println("SimpleDll fooState: "+t.getFooState ()); System.out.println("SimpleDll fooState: "+t.getFooState ()); } }
Compile your java class, then run like this:
D:\coligny\workspace\capsis4>java -cp class;ext\*;ext\windows64 -Djna.library.path="ext\windows64" capsis.lib.phelib.SimpleDllTest
Result:
D:\coligny\workspace\capsis4>java -cp class;ext\*;ext\windows64 -Djna.library.pa th="ext\windows64" capsis.lib.phelib.SimpleDllTest SimpleDll calling multiply ()... SimpleDll r: 9.299999 SimpleDll calling sumArray ()... SimpleDll r: 6.6000004 SimpleDll calling getVersion ()... SimpleDll r: 1.2.3 SimpleDll calling addValue ()... SimpleDll r: a[]: 6.6 7.7 8.8 SimpleDll calling foo methods... SimpleDll fooVersion: 4.5.6 SimpleDll fooState: 1.1 SimpleDll fooState: 1.2 SimpleDll fooState: 1.3000001 SimpleDll fooState: 1.4000001 SimpleDll fooState: 1.5000001