User Tools

Site Tools


documentation:java-c_connectioncompleteexample

A complete Java - C++ connection example with mingw/g++ and JNA under Windows 64 bits

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

Technical environment

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 installation

mingw is a c/c++ compiler for windows (Minimalist GNU for Windows, a GCC compiler port for Windows)

  • You may accept the proposed install directory (Program_Files…)
  • With the file explorer, goto the deepest installed dir (e.g. C:\Program Files\mingw-w64\x86_64-5.1.0-posix-seh-rt_v4-rev0) and launch the .bat (e.g. mingw-w64.bat) to open a mingw terminal knowing where the compiler commands are
  • You may compile .cpp programs from this mingw terminal

C Source code

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

C code compilation

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.

Check the dll with Dependency Walker

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?

  • Download DW from https://www.dependencywalker.com/ (no installation required).
  • Launch depends.exe, go to File, click Open… and open the desired DLL file.
  • In the Module section find the Module with the name of the DLL that you opened.
  • The CPU column tells if the file was compiled for 32 bits or 64 bits.

A Java class to wrap the dll

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);
 
	}
 
}

How to run it

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

Adding a C++ object, keeping a pointer to it, calling its methods

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
documentation/java-c_connectioncompleteexample.txt · Last modified: 2021/12/13 09:28 by 127.0.0.1