1

I am currently maintaining a Java communication library which wraps the functionality offered by some dll via JNI. At some point, in Java, I need to load the JNI wrapper to forward my requests and finally call the native library. Currently, this is accomplished by calling

System.loadLibrary("MyLibrary");

As stated here, this should always find MyLibrary if it is placed somewhere within java.library.path. Currently, my java.library.path seems to include some Java specific folders as well as all directories specified in the %PATH% environment variable:

C:\Program Files\Java\jdk1.8.0_45\bin;
C:\Windows\Sun\Java\bin;
C:\Windows\system32;
C:\Windows;
C:\Program Files\ImageMagick-6.9.0-Q16; 
C:\ProgramData\Oracle\Java\javapath;   
C:\Windows\system32;
C:\Windows;
C:\Windows\System32\Wbem;
C:\Windows\System32\WindowsPowerShell\v1.0\;
C:\Program Files\Microsoft SQL Server\110\Tools\Binn\;
C:\Program Files (x86)\Windows Kits\8.1\Windows Performance Toolkit\;
C:\Program Files (x86)\Microsoft SDKs\TypeScript\1.0\;
C:\texlive\2014\bin\win32;
C:\MyFolder\Common32;
C:\MyFolder\Common64;
C:\Program Files (x86)\Microsoft Team Foundation Server 2012 Power Tools\;
C:\Program Files (x86)\Microsoft Team Foundation Server 2012 Power Tools\Best Practices Analyzer\;.

My problem now is that even though MyLibrary is placed in C:\MyFolder\Common64; the above loadLibrary call yields an UnsatisfiedLinkError and I cannot seem to understand why. It is however found when I put it into the System32 folder, or if I instead call load while specifying the path absolutely:

System.load("C:\\MyFolder\\Common64\\MyLibrary.dll");

I tried to mess around with the java.library.path during runtime using the sys_path trick suggested in the answers given here and here. The following works flawlessly:

System.setProperty("java.library.path", "C:\\MyFolder\\Common64\\" );

Field fieldSysPath = ClassLoader.class.getDeclaredField( "sys_paths" );
fieldSysPath.setAccessible( true );
fieldSysPath.set( null, null ); 

System.loadLibrary("MyLibrary");

So, if I replace the entire java.library.path property with a custom path the dll is successfully loaded. However, this is not the desired behavior, as I want to find the dll dynamically by adding the correct directory to %PATH%. Furthermore, adding my custom path to java.library.path like so

String curJavaLibraryPath = System.getProperty("java.library.path");
System.setProperty("java.library.path", curJavaLibraryPath + ";C:\\MyFolder\\Common64\\" );     

also does not work.

Currently I am trying to make this work on a Win7 64-bit machine. My dll is also compiled as x64, if that's relevant.


The general procedure works flawlessly when I compile my Java library in x86 mode and copy the according JNI dll to C:\MyFolder\Common32\ and add that directory to %PATH%.

Community
  • 1
  • 1
pdresselhaus
  • 679
  • 14
  • 32
  • Is the jdk or jre running the application in 64bit? Have you tried to put the dll in the `C:\Windows\SysWOW64`directory? – aw-think May 04 '15 at 15:29
  • Loading the dll already works when I put it in the `System32` folder (on a x64 system this is the place where you put x64 libs). However, I want the dll to be found dynamically by letting the user put it wherever he/she wants and then adding an entry with the according folder to `PATH` – pdresselhaus May 04 '15 at 15:33
  • So, have you gone to this http://examples.javacodegeeks.com/java-basics/java-library-path-what-is-it-and-how-to-use/ and/or that https://www.chilkatsoft.com/java-loadLibrary-Windows.asp – aw-think May 04 '15 at 15:36
  • Yes, I know about the `-D` flag and also about setting the `java.library.path` via the IDE. But this does not help me as I only want to rely on the %PATH% variable. I just cannot figure out, why it would not load the dll even though it is already in the correct path. – pdresselhaus May 04 '15 at 15:40
  • What you exactly want to do? Put the file in the %PATH% or load it from a custom directory? If you want to put it on the "default" Windows %PATH%, then you have to put the dll to one of the folders listed by calling `echo %PATH%` at the command prompt. If you want to load it from a custom directory, you have to load it with System.load("Path to dll") and **not** with System.loadLibrary(). Or you have to modify your %PATH% Variable, but this sometimes needs a reboot of your machine. – aw-think May 04 '15 at 17:30
  • You get *what* `UnsatisfiedLinkError`? Do you really get it on `loadLibrary(),` or when calling a JNI method? – user207421 May 04 '15 at 22:38
  • I will elaborate on my question – pdresselhaus May 05 '15 at 09:07
  • Sounds like a typo to me. If not with the directory itself then maybe with the path separator. ":" does look a lot like ";" – user2543253 May 05 '15 at 10:02
  • Oh, and recently there was some other person where the PATH was too long. Does it work if you put you directory at the start? – user2543253 May 05 '15 at 10:44
  • Calling `System.setProperty("java.library.path", "C:\\MyFolder\\Common64\\;" + curJavaLibraryPath );` actually works for me. Consequently, I tried to move my custom directory to the beginning of %PATH%, which, to my surprise, did not have any effect, however... – pdresselhaus May 05 '15 at 12:03

1 Answers1

0

Apparently, the loadLibrary() Documentation has managed to fool me a bit. In case of an UnsatisfiedLinkError it states

UnsatisfiedLinkError - if the library does not exist.

Therefore, I obviously assumed that loadLibrary() at no point was capable of seeing my specified library. That is, however, not the case as this exception can also mean that it found the according library in a wrong (incompatible with the VM) bitness.

As far as I understand it, the only check you can do to find out which UnsatisfiedLinkError you have exactly, is to break down the error message contained by the exception. What I ended up doing is along the lines of:

try
{
    System.setProperty(JAVA_LIBPATH_PROPNAME, libpath);  
    ForceReloadLibraryPath();
    System.loadLibrary("AdsToJava");        
}
catch (UnsatisfiedLinkError ex)
{
    // We simply did not find the dll
    if (ex.getMessage().equals("no MyLibrary in java.library.path")) {

        // Just alert that nothing was found

    } else if ( (ex.getMessage().endsWith("Can't load IA 32-bit .dll on a AMD 64-bit platform")) || 
                (ex.getMessage().endsWith("Can't load AMD 64-bit .dll on a IA 32-bit platform")) ) {

        // Extract the path at which the dll whith the wrong bitness was found 
        // and remove it from the search path and try again
    }
}

You could obviously also pre-check the search path prior to calling loadLibrary but as this method also works in cases where users copy the dlls allover the search path I thought this to be the most powerful option.

pdresselhaus
  • 679
  • 14
  • 32
  • What is ForceReloadLibraryPath? – Adam Mudianto Jun 12 '20 at 20:42
  • Sorry mate. Neither do I remember after all these year, nor do I still have access to the project. Did you try without that call already? – pdresselhaus Jun 19 '20 at 10:18
  • ForceReloadLibraryPath is probably manipulating some cached private information in ClassLoader. See https://stackoverflow.com/questions/15409223/adding-new-paths-for-native-libraries-at-runtime-in-java for possible approaches (which depend on the target JVM version). – Jeremy K Sep 28 '20 at 19:29