Search code examples
javadlljna

Using JNA to access DLL: Output not in expected order


Background first, PROBLEM in bold:

I am a student and for a summer research project, I am going to write a Java program that lets me export data collected from research hardware into MATLAB in the Windows environment-- I don't need the program to be portable to other platforms. I have a third-party DLL with a great API containing C/C++ declarations, but I don't have access to the source code for the DLL.

I only know Java, so after looking at the available options, I decided to use JNA because it seemed easiest and most straightforward to use, plus, JNA is currently maintained. I asked a friend who knows C++ make an example DLL for me so I could start at the bottom and make a "hello world" style test of loading the library and accessing native methods using JNA. I followed examples from here-- and read about a hundred responses to other people's questions by twall and/or technomage.

After working around some UnsatisfiedLinkErrors due to jna.library.path starting as null, I recompiled the 32-bit example DLL given to me into 64-bit, and threw them both into the /bin folder, and I let the program determine which one to access, depending on the JVM running. I know I won't be able to do this with my third party DLL, which is 32-bit, but this is just part of my proof of concept phase.

I have been able to access the DLL methods easily enough, but I am seeing output-- all print statements-- in an unexpected order. What it looks like is that my Java code is executing start to finish, then the native library start to finish

Later in my project, I will need to call native functions, then java methods, then native functions again, so I need to understand what is causing this odd sequence early on. I tried a very procedural approach at first; below is my attempt to dress the methods up as objects I can manipulate, which yielded the same results. I am guessing it has to do with HOW the JVM and the native side of the house interact, or the differences in the way windows and Java handle I/O, but I am stumped on what is causing it, or how to approach the issue.

My setup looks like this: // CallFooPortably.java

public class CallFooPortably { //name taken from a tutorial, thanks!
    public static void main(String[] args) {
        System.out.println("MIAN START");// debug test
        System.setProperty("jna.library.path",System.getProperty("java.class.path"));
        System.out.println("MAIN Break1");// debug test
        methodOneClass objectA=new methodOneClass();
        objectA.methodOne();
        System.out.println("MAIN inter-method");// debug test
        objectA.methodTwo();
        System.out.println("MAIN END");// debug test
    }
}

My methods that call the libraries are here: // methodOneClass.java

public class methodOneClass{

      public methodOneClass(){
            System.out.println("Object Created!");
      }

      public void methodOne()  {
            String temp1 = System.getProperty("sun.arch.data.model");
            //provides the JVM bit-ness to the program 

            if (temp1.equals("32")) {
                  System.out.println("Break2");// debug test
                  UserDllLInterface.CLibrary.INSTANCE.Export1();
                  System.out.println("Break3");// debug test
                  UserDllLInterface.CLibrary.INSTANCE.Export2();
                  System.out.println("Break4");// debug test

            } else if (temp1.equals("64")) {
                  System.out.println("Break5");// debug test
                  UserDllLInterface.x64_CLibrary.INSTANCE.Export1();
                  System.out.println("Break6");// debug test
                  UserDllLInterface.x64_CLibrary.INSTANCE.Export2();
                  System.out.println("Break7");// debug test
            }
      }

      public void methodTwo()  {
            String temp2 = System.getProperty("sun.arch.data.model");
            if (temp2.equals("32")) {
                  System.out.println("Break8");// debug test
                  UserDllLInterface.CLibrary.INSTANCE.Export1();
                  System.out.println("Break9");// debug test
                  UserDllLInterface.CLibrary.INSTANCE.Export2();
                  System.out.println("Break10");// debug test
            } else if (temp2.equals("64")) {
                  System.out.println("Break11");// debug test
                  UserDllLInterface.x64_CLibrary.INSTANCE.Export1();
                  System.out.println("Break12");// debug test
                  UserDllLInterface.x64_CLibrary.INSTANCE.Export2();
                  System.out.println("Break13");// debug test
            }
      }
}

Lastly, the library interface is here: //UserDllLInterface.java

import com.sun.jna.Library;
import com.sun.jna.Native;

public interface UserDllLInterface {
      public static interface CLibrary extends Library { //comes from twall's examples, thanks!
            CLibrary INSTANCE = (CLibrary) Native.loadLibrary("DllExample",CLibrary.class);
            void Export1();
            void Export2();
      }

      public static interface x64_CLibrary extends Library {
            x64_CLibrary INSTANCE = (x64_CLibrary) Native.loadLibrary
              ("x64_DllExample", x64_CLibrary.class);
           void Export1();
           void Export2();
      }
}

my C++ source that was compiled into the 32-bit and 64-bit DLLs: //x64_DllExample.dll //DllExample.dll

#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>

bool APIENTRY DllMain(HANDLE hModule, DWORD reason, LPVOID lpReserved) {
    switch(reason) {
        case DLL_PROCESS_ATTACH:
            printf("You loaded me successfully!\n");
            break;
        case DLL_PROCESS_DETACH:
            printf("You unloaded me successfully!\n");
            break;
        case DLL_THREAD_ATTACH:
            printf("You threaded me successfully!\n");
            break;
        case DLL_THREAD_DETACH:
            printf("You un threaded me successfully!\n");
            break;
    }
    return true;
}

 extern "C" __declspec(dllexport) void Export1() {
     printf("You called the function Export1\n");
 }

extern "C" __declspec(dllexport) void Export2() {
    printf("You called the function Export2\n");
}

I expected to get output something like this, with the export functions mixed in:

MIAN START
MAIN Break1
Object Created!
Break5
You loaded me successfully!
You called the function Export1
You un threaded me successfully!
Break6
You called the function Export2
You un threaded me successfully!
Break7
MAIN inter-method
Break11
You called the function Export1
You un threaded me successfully!
Break12
You called the function Export2
You un threaded me successfully!
You unloaded me successfully!
Break13
MAIN END

But instead, I get output like this (Java first, then Windows):

MIAN START
MAIN Break1
Object Created!
Break5
Break6
Break7
MAIN inter-method
Break11
Break12
Break13
MAIN END
You loaded me successfully!
You called the function Export1
You called the function Export2
You called the function Export1
You called the function Export2
You un threaded me successfully!
You un threaded me successfully!
You un threaded me successfully!
You un threaded me successfully!
You unloaded me successfully!

Switching to the 64-bit JVM yields similar results, with Java code going first, followed by the native library. I am hoping someone can help shed light on why this happens. Thanks in advance for any help you can provide!


Solution

  • printf is buffered. If you add an fflush(stdout); after every printf, it would output as expected.