Introducing Java Native Access (JNA) 2 – Foreign (Function) Memory API


Compiling the C source code

Compiling the C source code is done via the G++ compiler and the command from the following figure:

Figure 7.5 – Compiling the C++ code

Or, as plain text:

C:\SBPBP\GitHub\Java-Coding-Problems-Second-Edition\Chapter07\P138_EngagingJNA>g++ -c                         “-I%JAVA_HOME%\include” “-I%JAVA_HOME%\include\win32” src/main/java/modern/challenge/cpp/Arithmetic.cpp –o jna/cpp/Arithmetic.o                       

Next, we can generate the proper native library.

Generating the native shared library

It is time to create the native shared library, math.dll. For this, we use G++ again as in the following figure:

Figure 7.6 – Generating math.dll

Or, as plain text:

C:\SBPBP\GitHub\Java-Coding-Problems-Second-Edition\Chapter07\P138_EngagingJNA>g++ -shared –o jna/cpp/math.dll jna/cpp/Arithmetic.o –static –m64             –Wl,–add-stdcall-alias

At this point, you should have math.dll in the jna/cpp folder.

Finally, run the code

Finally, we can run the code. If everything worked fine then you’re done. Otherwise, if you get an exception as java.lang.UnsatisfiedLinkError: Error looking up function ‘sumTwoInt’: The specified procedure could not be found then we have to fix it.But, what happened? Most probably, the G++ compiler has applied a technique referred to as name mangling (or, name decoration) – https://en.wikipedia.org/wiki/Name_mangling. In other words, the G++ compiler has renamed the sumTwoInt() method to something else that is not known to JNA.Solving this issue can be done in two steps. First, we need to inspect math.dll with a DLL Dependency Walker such as this one, https://github.com/lucasg/Dependencies. As you can see in the following figure, G++ has renamed sumTwoInt to _Z9sumTwoIntii (of course, on your computer could be another name):

Figure 7.7 – G++ has renamed sumToInt as _Z9sumTwoIntii

Second, we have to tell JNA about this name (_Z9sumTwoIntii). Basically, we need to define a Map containing the corresponding mapping of names and pass this map to a flavor of Native.load() that takes this map as the last argument. The code is straightforward:

public class Main {
  private static final Map MAPPINGS;
          
  static {
    MAPPINGS = Map.of(
      Library.OPTION_FUNCTION_MAPPER,
      new StdCallFunctionMapper() {
      Map<String, String> methodNames
        = Map.of(“sumTwoInt”, “_Z9sumTwoIntii”);
      @Override
      public String getFunctionName(
             NativeLibrary library, Method method) {
        String methodName = method.getName();
        return methodNames.get(methodName);
      }
    });
  }
  
  public static void main(String[] args) {      
    System.setProperty(“jna.library.path”, “./jna/cpp”);
    SimpleMath math = Native.load(Platform.isWindows()
      ? “math” : “NOT_WINDOWS”, SimpleMath.class, MAPPINGS);
      
    long result = math.sumTwoInt(3, 9);
    System.out.println(“Result: ” + result);
  }
}

Done! Now, you should obtain the result of 3+9. Feel free to explore JNA further, and attempt to use C/C++ structures, unions, and pointers.

Leave a Reply

Your email address will not be published. Required fields are marked *