# DLL aus jar extrahieren und als lib nutzen



## moccajoghurt (6. Nov 2011)

Hallo,
ich muss für mein aktuelles Projekt eine DLL aus einer jar extrahieren und dann mit der JNI nutzen:

So ist der Ablauf:
Runnable Jar wird ausgeführt -> DLL wird in in ein beliebiges Verzeichnis entpackt -> DLL wird mit System.load("DLL-Path") geladen.

Leider finde ich kein brauchbares Beispiel wie ich das mache. Google spuckt nur Ergebnisse aus, bei der die Klasse "ACWrapper" genutzt wird. Keine Ahnung was es damit auf sich hat (muss man die selber schreiben? kann man die wo downloaden? importieren kann ich sie jedenfalls nicht...).
Weiß jemand wie ich am besten vorgehe?
Gruß,
moccajoghurt


----------



## Marco13 (6. Nov 2011)

Da gibt's doch einen Haufen Beispiele dazu... brauchst du irgendwas spezielles? (In jocl.org - Java bindings for OpenCL hab' ich dafür die "LibUtils.java" Klasse, aber da ist ein bißchen was drumrum, weil er es aus der JAR oder als Datei laden kann)


----------



## moccajoghurt (6. Nov 2011)

Würde mir reichen, wenn die DLL einfach aus der Jar enptackt und dann geladen wird. Hast du vielleicht ein Codebeispiel?


----------



## Marco13 (6. Nov 2011)

Kannst mal die LibUtils.java auf GitHub anschauen - wenn das nicht hilft, kann man ja versuchen das noch auszudünnen...


----------



## moccajoghurt (6. Nov 2011)

Hab's jetzt folgendermaßen gelöst:

(nur der relevante Teil)

```
File new_dll = new File(file_path_string, "temp64.dll");

		try {
			InputStream istream = getClass().getClassLoader()
					.getResourceAsStream("temp64.dll");
			OutputStream ostream = new FileOutputStream(new_dll);

			byte buf[] = new byte[8192];

			int len;

			while ((len = istream.read(buf)) != -1) {
				ostream.write(buf, 0, len);
			}

			istream.close();
			ostream.close();

		} catch (Exception e) {

		}
```


----------



## Marco13 (6. Nov 2011)

OK, wenn Betreiebssystem, Speicherort der temprorären Datei (und deren mögliches schon-Vorhandensein) und Fehlerbehandlung(!) keine Rolle spielen ist's wirklich nicht viel


----------



## moccajoghurt (6. Nov 2011)

Ok ich habe ein neues Problem:

Wenn ich das ganze von Eclipse aus starte funktioniert alles. Wenn ich das Projekt dann allerdings als jar exportiere, dann kann er die DLL nicht laden, obwohl sie im entsprechenden Pfad liegt.
Also nach:

```
static {
System.load(System.getProperty("user.home") + "\\Documents\\temp-0.0\\temp64.dll");
}
```

Bricht das Programm ab. Allerdings liegt die DLL definitiv in diesem Ordner (ich hab nachgeschaut).

Woran liegt das und wieso passiert das nur, wenn ich mein Programm als Runnable Jar starte?


----------



## XHelp (6. Nov 2011)

1. Was heißt "Bricht das Programm ab"?
2. temp-0.0 in Dokumenten, im ernst?

P.S. Irgendwann mal habe ich sowas ähnliches geschrieben:

```
public class NativeLibLoader {
        private static final String LIB_NAME = "somelib-";
        private static final String LIB_PATH_IN_JAR = "/lib/";
        static {
                loadFromLibraryPath(LIB_NAME+System.getProperty("os.arch"));
        }
        
        private static void loadFromLibraryPath(String libraryName) {
                try {
                        System.loadLibrary(libraryName);
                } catch (UnsatisfiedLinkError e) {
                        e.printStackTrace();
                        loadFromJar(libraryName);
                }
        }


        private static void loadFromJar(String libraryName) {
                long randomPart = new Date().getTime();
                File libFile = new File(System.getProperty("java.io.tmpdir") + "/"
                                + "JST_" + randomPart + LIB_PATH_IN_JAR + libraryName + ".dll");
                InputStream in = NativeLibLoader.class
                                .getResourceAsStream(LIB_PATH_IN_JAR + libraryName + ".dll");
                try {
                        if (libFile.getParentFile().mkdirs()) {
                                FileOutputStream fw = new FileOutputStream(libFile);
                                byte[] b = new byte[1024];
                                int noOfBytes = 0;
                                while ((noOfBytes = in.read(b)) != -1) {
                                        fw.write(b, 0, noOfBytes);
                                }
                                in.close();
                                fw.close();
                        }


                } catch (IOException e) {
                        e.printStackTrace();
                }
                System.load(libFile.toString());
        }
}
```

vlt hilft es dir ja irgendwie weiter


----------



## moccajoghurt (6. Nov 2011)

Hab das Verzeichnis zum testen gewählt, weil es wenig Tipparbeit ist. Wenn alles funktioniert ändere ich das. 

Naja ich habe zum Testen einen JFrame erstellt, der angezeigt wird (wusste nicht wie ich bei einer Jar testen soll wo das Programm aufhört zu funktionieren). 

So wird der Frame angezeigt:

```
static {
		// Frame zum Test, ob bis hierhin alles funktioniert hat
		JFrame frame = new JFrame();		
		frame.setLocationRelativeTo(null);
		frame.setSize(200,300);
		frame.add(new JLabel(System.getProperty("user.home") + "\\Documents\\temp-0.0\\temp64.dll"));
		frame.setVisible(true);
		//jetzt die DLL laden
		System.load(System.getProperty("user.home") + "\\Documents\\temp-0.0\\temp64.dll");
	}
```

So dann nicht mehr:

```
static {
		//jetzt die DLL laden
		System.load(System.getProperty("user.home") + "\\Documents\\temp-0.0\\temp64.dll");
		// Frame zum Test, ob bis hierhin alles funktioniert hat
		JFrame frame = new JFrame();		
		frame.setLocationRelativeTo(null);
		frame.setSize(200,300);
		frame.add(new JLabel(System.getProperty("user.home") + "\\Documents\\temp-0.0\\temp64.dll"));
		frame.setVisible(true);
	}
```

Daher muss es wohl an dem Laden der DLL scheitern.


----------



## moccajoghurt (6. Nov 2011)

Der einzige Unterschied zwischen deiner Methode und meiner ist, dass meine DLL nicht ins java tmpdir geladen wird, sondern eben in home-Verzeichnis. Also die Datei ist definitiv da, es muss irgendwie an 
	
	
	
	





```
System.load(System.getProperty("user.home") + "\\Documents\\temp-0.0\\temp64.dll");
```
in Verbindung mit der Runnable Jar liegen. Schließlich ist die DLL ja auch ansprechbar, wenn ich das Programm von Eclipse aus starte.
Ich kanns mir nicht erklären.


----------



## XHelp (6. Nov 2011)

Kannst ja vernunftige catch-Blöcke schreiben und schauen, was wirklich schief geht.
Oder eben dein Programm aus der Konsole starten


----------



## moccajoghurt (6. Nov 2011)

Irgendwas stimmt da nicht. Wenn ich die Jar mit 
	
	
	
	





```
java -jar blub.jar
```
 starte, wird sie zwar ausgeführt, aber ich sehe keine ausgaben.
Habe sogar extra einen Fehler eingebaut, damit eine Exception geworfen wird, aber es wird nichts angezeigt.


----------



## moccajoghurt (6. Nov 2011)

Ok es hängt irgendwie mit den anderen Klassen in meinem Projekt zusammen.
Habe jetzt nur die für die DLL relevanten klassen in ein neues Projekt gezogen. Wenn ich diese jar starte werden sowohl Ausgaben angezeigt, sowie die DLL geladen (obwohl es diesmal per jar gestartet wurde).
Ist es irgendwie möglich, dass die andern Klassen sich gegenseitig stören? (Ich habe als Startpunkt nur die für die DLL relevante Klasse angegeben, die die andern Klassen eigentlich komplett in Ruhe lässt).


----------



## moccajoghurt (7. Nov 2011)

Ok hab den Fehler gefunden.
Die DLL war in 64bit kompiliert. Wenn ich das Programm von Eclipse aus starte arbeitet Java in 64bit, aber wenn ich es als Runnable Jar ausführe, arbeitet die JVM in 32bit.
Hab es jetzt so gelöst, dass ich mit 
	
	
	
	





```
System.getProperty("sun.arch.data.model")
```
 abfrage ob es 32bit oder 64 bit sind und habe eine 32bit und eine 64bit kompilierte DLL und lade dann die entsprechende.


----------



## Marco13 (7. Nov 2011)

Fast wie bei den LibUtils  (Außer dass es wohl nur um Windows geht...)


----------



## moccajoghurt (7. Nov 2011)

Ach MIST da denkt man, man hat das Problem gelöst und dann gibts doch wieder was Neues. Also bei mir läuft alles, aber hab die Jar mal probeweise auf einem anderen Rechner gestartet und da kommt auf einmal nen ganz anderer Fehler:

```
java.lang.UnsatisfiedLinkError: C:\Users\Tim\Documents\temp-0.0\temp32.dll: Can't find dependent libraries
```
Die DLL lag aber im Ordner, also bin ich wieder ratlos.


----------



## Spacerat (7. Nov 2011)

Vermutlich greift tmp32.dll ihrerseits auf andere dynamisch verlinkte DLLs zu, die entweder gar nicht oder nicht korrekt installiert wurden.


----------



## moccajoghurt (7. Nov 2011)

Hmm eigentlich besteht die DLL aus nur einem Header und einer Source, in der ich folgendes include:

```
#include "stdafx.h"
#include <WinUser.h>
#include <Windows.h>
#include <jni.h>
#include <iostream>
#include "ReceiveKeys.h"
```

Visual Studio hat da aber neben meinen JNI Header "stdafx.h" und "targetver.h" standardmäßig dazugepackt.
Neben meiner eigenen Source wurde noch "dllmain.cpp" und "stdafx.cpp" erstellt, bei denen ich aber nichts modifiziert habe.

Wie finde ich denn heraus welche dynamisch verlinkte DLLs meine benutzt und füge die so hinzu, dass ich mein Programm auch auf andern Rechner ausführen kann?


----------



## Spacerat (7. Nov 2011)

Also bis auf dieses ReceiveKeys (RK) ist ja meines Wissens alles Standard und RK dürfte dabei wohl deine eigene Kreation sein. Aber irgendwo dort sollte was zu finden sein. Wenn nicht, Dependency Walker fragen.


----------



## Marco13 (7. Nov 2011)

Wenn du ohenhin schon Visual Studio installiert hast, kannst du die dependencies auch mit
dumpbin /DEPENDENTS dieDatei.dll
rausfinden, da braucht's keine full-bloated software mehr dafür.


----------



## moccajoghurt (7. Nov 2011)

```
dumpbin /DEPENDENTS temp32.dll
```
Hat bei mir folgende Ausgabe gebracht:

```
Dump of file C:\Users\Marius\Documents\temp-0.0\temp32.dll

File Type: DLL

  Image has the following dependencies:

    KERNEL32.dll
    USER32.dll
    MSVCR100D.dll

  Summary

        1000 .data
        1000 .idata
        2000 .rdata
        1000 .reloc
        1000 .rsrc
        4000 .text
       10000 .textbss
```

Was davon könnte jetzt auf nem anderen Rechner fehlen? Muss ich die dependent DLLs vielleicht einfach mitkopieren?
Allerdings liegen diese 3 DLLs bei mir alle in System32... wie ist es also möglich, dass meine DLL diese nicht findet...?


----------



## Marco13 (7. Nov 2011)

Schau mal ob auf dem Rechner, auf dem es NICHT geht, die MSVCR100D fehlt (übrigens die Debug-Version - solltest vielleicht im Release-Modus compilieren). Websuche liefert da viele Ergebnisse, aber ich würde nicht irgendeine DLL von einer Dubiosen Seite verwenden - ist diese DLL vielleicht Teil dieser "VC  Redistributables" ???:L Ich würd' mal unter Redistributing Visual C++ Files anfangen zu suchen (oder nach einer Möglichkeit suchen, dafür zu sorgen, dass diese DLL nicht mehr benötigt wird - da gibt's im Web Hinweise dazu, aber was konkretes kann ich auf die Schnelle nicht sagen)


----------



## moccajoghurt (7. Nov 2011)

Ok ich hab nachgeschaut, dem Rechner auf dem es nicht geht ist keine MSVCR100D.dll. Kann ich einfach meine MSVCR100D.dll in die Jar kopieren und zusammen mit meinen eigenen DLLs entpacken?
PS: Auf dem Rechner auf dem es nicht geht liegt aber eine MSVCR100.dll (ohne das D am Ende)... hmm.


----------



## Marco13 (7. Nov 2011)

Ahja, das sollte schon reichen: Schau mal, dass du deine temp-DLL im Release-Modus compilierst (im einfachsten Fall oben in der Combo-Box auswählen), dann sollte er auf die ohne "D" verweisen und die entsprechend auch finden.


----------



## moccajoghurt (7. Nov 2011)

Jo thx, ich denke so würde es auch gehen. Habe es gerade so gelöst, dass ich Multi-Threaded kompiliere. Dadurch bestehen irgendwie auch keine Abhängigkeiten mehr. Ich weiß leider nicht genau was genau das jetzt bedeutet, aber es funktioniert.
Danke für deine Hilfe hat mir sehr weitergeholfen.


----------

