Problema ao carregar biblioteca com JNI

Estou enfrentando um problema ao carregar a DLL da minha biblioteca JNI.

Eu fiz uma biblioteca, para aprendizado, que permite ao meu programa Java chamar várias funções através da Win32 API, como: MAC address, tamanho do espaço livre em disco, serial do HD, nome da placa de rede, etc.

Coloco a minha DLL na pasta Windows\System32 e tudo funciona perfeitamente. Porém eu observei que vários projetos colocam as DLLs na mesma pasta do JAR que as chama, como por exemplo o SWT e o Systray.

Como eu pretendo distribuir essa biblioteca, eu queria manter a complexidade no mínimo possível, sem mexer em pastas do Sistema Operacional, deixando a DLL na mesma pasta do meu JAR, mas sem usar a diretiva -D do java.exe.

Olhando o código dessas bibliotecas eu vi que eles carregam as DLLs com o comando:

System.loadLibrary

que requer que a DLL esteja em:

java.library.path

Eu já olhei em todo canto, mas não consegui entender como nessas biblioteca eles fizeram para colocar as DLLs na mesma pasta do JAR. Quando eu tento fazer o mesmo eu obtenho a seguinte exceção:

Exception in thread "main" java.lang.UnsatisfiedLinkError: no winsi.dll in java.library.path

Alguém já passou pelo mesmo problema? Agradeço qualquer ajuda, porque já estou ficando louco, estou a ponto de apagar tudo e esquecer esse negócio de JNI. :x

jah tentou utilizar o System.setProperty?

System.setProperty("java.library.path","dlls");

[quote=FLC]jah tentou utilizar o System.setProperty?

System.setProperty("java.library.path","dlls");

[/quote]
Não funciona. É possível mudar o valor da propriedade, mas a JVM não recarrega os valores.

Dei uma olhada mais a fundo no SWT e vi que p/ rodar é preciso passar o -D, vou usar assim mesmo.

[quote=fcmartins]
Não funciona. É possível mudar o valor da propriedade, mas a JVM não recarrega os valores.[/quote]

hummm… q coisa… achava que chamando o loadLibrary depois de dar um setProperty pegava-se a propriedade atual. Bom saber disso.

Eu nem cheguei a testar, mas quando eu estava pesquisando como resolver meu problema tinha um monte de gente que tinha tentado isso e não tinha conseguido. O que pode ser feito é pegar o diretório atual do JAR (não tenho idéia de como fazer programaticamente) e carregar a DLL com System.load, passando o nome completo do arquivo.

[quote=fcmartins]Olhando o código dessas bibliotecas eu vi que eles carregam as DLLs com o comando:

System.loadLibrary

que requer que a DLL esteja em:

java.library.path

java.library.path é atualizado pela variável PATH do sistema. Você poderia atualizar a variável PATH do SO.
Normalmente java.library.path inclui o diretório atual “.”, então daria pra usar a .dll no mesmo diretório do jar.

  • Editado: se você usar -Djava.library.path ele vai reescrever a propriedade e usar apenas o que você indicou.

Outra alternativa é usar:

File dll = new File("lib/minhalib.dll"); // minhalib.dll no subdiretório lib System.load(dll.getAbsolutePath());

[quote] Normalmente java.library.path inclui o diretório atual “.”, então daria pra usar a .dll no mesmo diretório do jar[/quote]O . referencia o local de onde você rodou o java. Se eu rodar um .jar executável funciona, mas não em outros casos.

[quote]File dll = new File("lib/minhalib.dll"); // minhalib.dll no subdiretório lib System.load(dll.getAbsolutePath());[/quote]Só que aí você está assumindo que o jar está no diretório .lib, nem sempre é/será o caso.

O problema, a meu ver, é que o funcionamento da carga das bibliotecas com JNI é muito mal-feito. No google existem várias pessoas que passam pelos memos problemas que eu, é gente que usa SWT e pena p/ distribuir a aplicação com o WebStart, é gente que faz toda sorte de gambiarras p/ poder empacotar uma dll dentro de um .jar, é gente com problema de Dll hell, etc.

Se a JVM carregasse as bibliotecas da mesma forma que os .jars, através do CLASSPATH e permitisse empacotar as bibliotecas dentro de um .jar e acessá-las como um resource todos os problemas estariam resolvidos.

É ridículo esperar que um programador ou usuário do Java fique mexendo em Windows/System32 (ou o equivalente do Linux) ou na pasta de instalação da JDK, sem falar dos Web-containers. Java foi feito p/ ser simples, se eu crio uma biblioteca JNI, o usuário tem que copiar e colar e sair usando.

De toda forma, tem o fórum do Mustang no Java.net, e eu dei minha opinião lá, nos posts abertos que discutem esses problemas, quem puder dê um pitaco também, quem sabe não ajuda:
:arrow: Make JNI simpler to use
:arrow: Invoke JNI DLL if packed in same JAR

O que já verifiquei na prática é:

  • DLLs JNI precisam de um instalador, que as ponha no local correto (que ainda acho que é no C:\Windows\System32 - podem me xingar à vontade…) - não dá problema de path ou de não ser encontrada na sua instalação do JDK ou JRE, ou mesmo se você está usando Microsoft JVM, que suporta JNI também.
  • Quando você usa DLLs JNI deve-se preparar para problemas. Deve-se reduzir ao máximo a funcionalidade que elas executam diretamente. Por exemplo, nunca usaria uma DLL JNI para criptografia (que pode ser perfeitamente feita em Java, sem problemas); no máximo, para aquelas coisas que realmente não dá para fazer em Java, como chamar algum ActiveX que você realmente precisa chamar para fazer seu sistema funcionar.) Além disso, não se deve chamar DLLs JNI dentro de um servidor de aplicações ou de um ‘web container’; deve-se deixar as chamadas segregadas em uma aplicação Java separada (com a qual comunica-se via Sockets ou RMI), para que os problemas em código nativo só “detonem” essa aplicação, não o servidor completo.