[Erledigt] Probleme mit JNI: UnsatisfiedLinkError

Ich möchte eine Methode aus Performancegründen nativ auslagern. Zu Hause ist mir das auch schon gelungen. Da hab ich allerdings keine packages und statischen Methoden verwendet.

Mein Code:

MUtilities (package: utils)


import [...]

public class MUtilities {
// CONSTANTS
	public static final int OVERWRITE_FILES = 0;
	public static final int UNCHANGE_EXISTING_FILES = 1;
	
	public static native int countFilesInDirectory( String dir );
	
	private static boolean loadLib = true;
	
	static {
		try {
			String lib = new File(".").getAbsolutePath() +
			File.separatorChar +
			"native" +
			File.separatorChar +
			"counter." +
			((System.getProperty("os.name").equals("Linux")) ? "so" : "dll");
			
			System.load( lib );
			
			System.out.println( lib );
		} catch ( java.lang.UnsatisfiedLinkError err ) {
			loadLib = false;
			System.err.println( "Kann Library nicht laden!" );
		} 
	}

	[...]
}```

**listing.cpp**
```#include <iostream>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>

#include <jni.h>
#include "MUtilities.h"

int count_files( const char* path ) {
	DIR* dir;
	struct dirent* dirpointer;
	
	if ( (dir=opendir(path)) == NULL ) {
		std::cerr << "Verzeichnis konnte nicht geoeffnet werden!" << std::endl;
		return 0;
	}
	
	int i=0;
	
	char* tmp_file = new char[500];
	struct stat* file_info;
	while ( (dirpointer=readdir(dir)) != NULL ) {
		if ( strcmp(dirpointer->d_name, ".") && strcmp(dirpointer->d_name, "..") ) {
			strncpy( tmp_file, path, 450 );
			strncat( tmp_file, "/", 1 );
			strncat( tmp_file, dirpointer->d_name, 49 );
			
			file_info = new struct stat;
			
			if ( stat(tmp_file, file_info) != -1 && file_info->st_mode & S_IFDIR ) {
				i += count_files( tmp_file );
				//printf( "%s
", tmp_file );
			} else {
				i++;
			}
			
			delete file_info;
		}
	}
	
	closedir( dir );
	
	return i;
}

JNIEXPORT jint JNICALL Java_utils_MUtilities_countFilesInDirectory( JNIEnv* env, jclass obj, jstring str ) {
	return count_files(  env->GetStringUTFChars(str, NULL)  );
}```

**MUtilities.h** (generiert durch **javah -jni utils.MUtilities**)
```/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class utils_MUtilities */

#ifndef _Included_utils_MUtilities
#define _Included_utils_MUtilities
#ifdef __cplusplus
extern "C" {
#endif
#undef utils_MUtilities_OVERWRITE_FILES
#define utils_MUtilities_OVERWRITE_FILES 0L
#undef utils_MUtilities_UNCHANGE_EXISTING_FILES
#define utils_MUtilities_UNCHANGE_EXISTING_FILES 1L
/*
 * Class:     utils_MUtilities
 * Method:    countFilesInDirectory
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_utils_MUtilities_countFilesInDirectory
  (JNIEnv *, jclass, jstring);

#ifdef __cplusplus
}
#endif
#endif```

Das Programm lässt sich erfolgreich übersetzen mit

mingw32-g++ -I C:\Programme\Java\jdk1.6.0\include -shared -o counter.dll listing.cpp



Jetzt liegt die Library 'counter.dll' im Verzeichnis 'native' in meinem Hauptprogrammordner.
Beim Programmstart wird die DLL auch erfolgreich geladen, doch beim Aufruf der nativen Methode kommt ein UnsatisfiedLinkError:

Exception in thread “main” java.lang.UnsatisfiedLinkError: utils.MUtilities.countFilesInDirectory(Ljava/lang/String;)I
at utils.MUtilities.countFilesInDirectory(Native Method)
at main.Starter.main(Starter.java:66)




Wo kann da das Problem liegen? :confused:

Das Problem ist, denke das wirst Du auch wissen, das er Deine native Funktion nicht findet. Du lädst die native lib über den absoluten Pfad und mit System.load(); Das ist ja auch erstmal legitim aber versuche lieber erstmal die native lib in Dein classpath mit aufzunehmen und über System.loadLibrary(„nativelib“); zu laden. Erstens brauchst Du Dich dann nicht mehr um die extension kümmern und die native lib kann so in Deinem package liegen und am Ende mit in ein eventuell späteres jar-file gepackt werden.

Versuche also erstmal den „Normalen“ Weg.

Gut Schuß
VuuRWerK :wink:

Funktioniert ebenfalls nicht. Selbst wenn die DLL direkt ins Root-Verzeichnis von meinem Projektordner kopiere, lädt er zwar die Library, kann aber nicht auf die native Funktion zugreifen.

Der Befehl zum laden einer DLL ist eigentlich nicht
System.load(“library.dll”);
sondern
System.loadLibrary(“library”); // Ohne Dateiendung.

@Marco13: Das habe ich ja schon geschrieben dass das der „normale“ Weg ist. Man kann aber auch eine externe(extern im Sinne von ausserhalb des CP) Lib laden mit System.load(„path/to/lib“);

Wenn aber die Lib geladen wird aber die native Funktion nicht gefunden scheint es mit dem Aufruf etwas nicht zu stimmen.

Hast Du es aber schon mit dem library-path versucht?

-Djava.library.path=C:/path/to/lib

Dann aber wieder mit System.load(„lib“) laden.

Gut Schuß
VuuRWerK :wink:

[QUOTE=VuuRWerK]Hast Du es aber schon mit dem library-path versucht?

-Djava.library.path=C:/path/to/lib

[/QUOTE]

Klar, genauso hab ich es gemacht und die Lib konnte auch erfolgreich über System.loadLibrary() geladen werden!
Ich glaube der Hund liegt bei den Packages begraben …
Ohne Packages kann ich auf die nativen Methoden zugreifen :frowning:

Ich hatte da auch mal ein Problem mit Packages gehabt konnte es aber wie gesagt dahingehend lösen das ich, wie zu anfangs es beschrieben, die native lib in den CP aufgenommen habe und dann einfach mit System.load(„lib“); geladen.

Entwickelst Du zufällig mit Netbeans? Wenn ich mit JNI arbeite nutze ich meistens Netbeans da ich dafür mal ein How To gefunden habe welches beschreibt wie man in Netbeans das Java und Native Projekt zusammen verwalten kann. Guggst Du hier. Aber auch das dürfte interessant sein.

Geht sicherlich auch mit Eclipse.

Gut Schuß
VuuRWerK :wink:

Wie schaut es denn aus, bist Du weiter gekommen?

Gut Schuß
VuuRWerK :wink:

Das mit dem Classpath funzt leider auch nicht, ich kriegs unter Windows einfach nicht gebacken :frowning:
Hauptsache ich probiers heute unter Linux (Ubuntu 8.04) und es funktioniert problemlos! o_O
Ist es möglich, dass unter Windows die JNI-Headerdateien fehlerhaft sind, oder mein Compiler (mingw32-g++) einfach nicht mit JNI kompatibel ist??
Unter Ubuntu verwende ich g++ Version 4.2.3.

EDIT: Ah, das sieht interssant aus: Java Native Interface (JNI) - Creating JNI DLL on WinXP using MinGW and GCC

Mit dem Compiler hat das nichts zu tun genauso hat das auch nix mit JNI zu tun denn JNI ist auch nur in C implementiert und hat keinerlei Abhängigkeiten.

Ich schau mal ob ich heute Abend Zeit finde um mal ein Beispiel zusammen zusammen zu bauen. Vielleicht hilft es Dir und Du kannst es verwenden.

Gut Schuß
VuuRWerK :wink:

Lösung gefunden :smiley:
Der MinGW-Compiler braucht zusätzlich den Parameter -Wl,–add-stdcall-alias damit er richtig linkt!

Mit diesem Befehl klappts:

mingw32-g++ -I C:\Programme\Java\jdk1.6.0\include -Wl,–add-stdcall-alias -shared -o counter.dll listing.cpp

Btw.: Wie markiert man einen Thread als gelöst?

Na siehst Du ist doch alles so einfach hier :slight_smile:

Ganz oben in den Themen-Optionen auf „Mark this thread as solved“ klicken.

Gut Schuß
VuuRWerK :wink: