DLL aus jar extrahieren und als lib nutzen

moccajoghurt

Bekanntes Mitglied
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
 
Hab's jetzt folgendermaßen gelöst:

(nur der relevante Teil)
Java:
		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) {

		}
 
OK, wenn Betreiebssystem, Speicherort der temprorären Datei (und deren mögliches schon-Vorhandensein) und Fehlerbehandlung(!) keine Rolle spielen ist's wirklich nicht viel 😉
 
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:
Java:
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?
 
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:
Java:
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
 
Zuletzt bearbeitet:
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:
Java:
	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:
Java:
	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.
 
Zuletzt bearbeitet:
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
Java:
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.
 
Kannst ja vernunftige catch-Blöcke schreiben und schauen, was wirklich schief geht.
Oder eben dein Programm aus der Konsole starten
 
Irgendwas stimmt da nicht. Wenn ich die Jar mit
Code:
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.
 
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).
 
Zuletzt bearbeitet:
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
Java:
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.
 
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:
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.
 
Vermutlich greift tmp32.dll ihrerseits auf andere dynamisch verlinkte DLLs zu, die entweder gar nicht oder nicht korrekt installiert wurden.
 
Hmm eigentlich besteht die DLL aus nur einem Header und einer Source, in der ich folgendes include:
Code:
#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?
 
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.
 
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.
 
Code:
dumpbin /DEPENDENTS temp32.dll
Hat bei mir folgende Ausgabe gebracht:
Code:
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...?
 
Zuletzt bearbeitet:
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)
 
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.
 
Zuletzt bearbeitet:
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.
 
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.
 

Neue Themen


Zurück
Oben