C Wrappering with JNA
November 27, 2009
I recently had the job of talking to the Quick Address Pro V6 API from Java. The API is only available as a C library, so I had to use some C wrappering to access it. I did this using JNA. It was an interesting challenge to get it working on both Solaris and Windows.
Interface
In order to use the JNA library, you create an inner interface which mirrors the Java source. In the interface, you load a library into an INSTANCE variable. Here's a cut down example including a call to QA_Open:
public interface QasLibrary extends Library { QasLibrary INSTANCE = (QasLibrary) Native.loadLibrary(Platform.isWindows() ? "qaupied" : "qaupicd", QasLibrary.class); int QA_Open(byte[] vsIniFile, byte[] vsSection, IntByReference riHandle); }
Then, in the class that this interface is inside, create methods to call through to the interface:
public static void Open(byte[] vsIniFile, byte[] vsSection, IntByReference riHandle) { int lReturn = QasLibrary.INSTANCE.QA_Open(vsIniFile, vsSection, riHandle); QasInterface.handleError("QA_Open", lReturn); }
This can then be wrapped by other code to do type conversions:
public static int Open(String pIniFilePath, String pIniFileSection) { IntByReference lHandle = new IntByReference(); byte[] lIniFileName = pIniFilePath.getBytes(); byte[] lSection = pIniFileSection.getBytes(); QasInterface.Open(lIniFileName, lSection, lHandle); return lHandle.getValue(); }
Issues
We had some issues with big endian/little endian for numbers on Solaris. We ended up solving that with this code:
public static long GetPickListSize(int viHandle) { byte[] lStrResult = new byte[10]; LongByReference lResultsSize = new LongByReference(); QasInterface.GetSearchStatusDetail(viHandle, qassint_PICKLISTSIZE, lResultsSize, lStrResult, 10); long lResult = lResultsSize.getValue(); if (Platform.isWindows() == false) { lResult = lResult >> 32 + ((lResult & 0xFFFFFFFF) << 32); } return lResult; }
Null Terminated Strings
We also needed to null terminate strings passed into the interface.
byte[] lIniFileName = (pIniFilePath + " ").getBytes(); byte[] lSection = (pIniFileSection + " ").getBytes(); // null terminate the strings lIniFileName[lIniFileName.length - 1] = 0; lSection[lSection.length - 1] = 0;