2012-04-05 38 views
16

Ho creato un modulo C++ per creare un file di libreria condiviso e quindi chiamarlo da Java usando JNI.Il mio codice dll funziona da un file exe, ma non riesce a caricare da Java loadLibrary

Ho 2 ambienti, Windows e Unix e ho un programma eseguibile in C++ e un programma Java che ricompongo solo per ogni ambiente.

  • Quando compilo il mio programma tester.exe in Unix ed eseguirlo con metodi dalla mia biblioteca (.so) funziona benissimo.
  • Quando compilo il mio programma Java in Unix e carico la mia libreria (.so) con Java loadLibrary, funziona perfettamente.
  • Quando compilo il mio programma tester.exe in Windows e lo eseguo utilizzando i metodi dalla mia libreria (.dll) funziona correttamente. Proprio come la versione unix .

  • Quando compilo il mio programma Java in Windows e carico la mia libreria (.dll) con loadLibrary di Java, fallisce. Dice Tentativo di accedere all'indirizzo non valido .

io non riesco a capire il motivo per cui non funziona con Java loadLibrary durante l'esecuzione in Windows, ma funziona ovunque utilizzando lo stesso codice. Se ritardo caricare una DLL dipendente che utilizza la mia libreria, quindi la mia libreria carica in Java ma non è funzionale. So che esiste un codice specifico che causa il problema con Java che carica la mia libreria, ma non riesco a capire perché il mio exe C++ non abbia problemi con gli stessi metodi e le stesse librerie.


La mia DLL ha 1 metodo esposto che chiama 4 metodi da alcune librerie esistenti. Se commento questi 4 metodi, allora la mia DLL viene caricata in Java bene. So che è qualcosa a che fare con questi metodi da una libreria a cui collego la mia DLL. C'è qualcosa di diverso nel modo in cui Java vede le librerie dipendenti? Ho provato a caricare prima le librerie dipendenti, ma uno dei file DLL che carico causa un errore di ricorsione e lo stack trabocca.

Qualcuno sa come aggirare una DLL che provoca un overflow dello stack da un errore di ricorsione? Ho bisogno dei metodi, ma non riesco a caricarlo con java loadLibrary.


Ecco ulteriori dettagli sui file coinvolti e il messaggio di errore effettivo. Ho aggiunto un DllMain al mio file dll inital solo per vedere cosa carica e quando. Se compilo lo stesso programma (my_plain_dll_to_call_JNI_DLL) come un file exe, tutto funziona correttamente. Se lo compilo e lo carico dal mio programma java ciò accade.

  • myJavaProgram, chiama semplicemente System.loadLibrary() per caricare un file di base dll che chiama un metodo nel mio altre dll che contiene il codice JNI.
  • my_plain_dll_to_call_JNI_DLL è una dll che ho creato collegandola al mio file di libreria dll solo per testare la dipendenza. Si chiama solo un metodo dall'altra DLL che sta chiamando il codice nativo di cui ho bisogno.
  • my_JNI_DLL.ll è un file dll collegato alle librerie C++ esistenti di programmazione a cui è necessario accedere da JNI. Contiene chiamate dirette ai metodi nelle librerie di codice sorgente esistenti.

ho scritto il nome del file che visualizza il testo a fianco di ogni linea per mostrare quale livello l'esecuzione è in.

 

c:\java myJavaProgram 
myJavaProgram: Java Static Method Entry. 

myJavaProgram: Java Calling System.loadLibrary(my_plain_dll_to_call_JNI_DLL) 

my_JNI_DLL.dll: Entering DllMain 

my_JNI_DLL.dll: DLL_PROCESS_ATTACH 

my_plain_dll_to_call_JNI_DLL: DLL_PROCESS_ATTACH 
my_plain_dll_to_call_JNI_DLL: DLL_THREAD_ATTACH 
my_plain_dll_to_call_JNI_DLL: DLL_THREAD_DETACH 
my_plain_dll_to_call_JNI_DLL: DLL_PROCESS_DETACH 

myJavaProgram: my_plain_dll_to_call_JNI_DLL Loaded! 

myJavaProgram: Java Static Method Exit. 

myJavaProgram: Entering Main(). 

my_plain_dll_to_call_JNI_DLL: In call_my_JNI_DLL_method 

my_JNI_DLL.dll: In my_JNI_DLL_method 

my_JNI_DLL.dll: Entering my_JNI_DLL_CheckEnvironmentVariables() 

my_JNI_DLL.dll: Exiting my_JNI_DLL_CheckEnvironmentVariables 

my_JNI_DLL.dll: Calling StartExistingNativeCode. 

# 
# A fatal error has been detected by the Java Runtime Environment: 
# 
# Internal Error (0xc0fb007e), pid=7500, tid=7552 
# 
# JRE version: 6.0_21-b06 
# Java VM: Java HotSpot(TM) Client VM (17.0-b16 mixed mode, sharing windows-x86) 
# Problematic frame: 
# C [KERNELBASE.dll+0x9673] 
# 
# An error report file with more information is saved as: 
# C:\hs_err_pid7500.log 
# 
# If you would like to submit a bug report, please visit: 
# http://java.sun.com/webapps/bugreport/crash.jsp 
# The crash happened outside the Java Virtual Machine in native code. 
# See problematic frame for where to report the bug. 
# 

my_plain_dll_to_call_JNI_DLL: DLL_PROCESS_DETACH 

my_JNI_DLL.dll: Entering DllMain 

my_JNI_DLL.dll DLL_PROCESS_DETACH 
 

Aggiornamento Ho ridotto la questione una libreria di gestione della memoria collegata da un'altra DLL che il mio programma utilizza. La DLL che usa è sh33w32.dll, si chiama SmartHeap ed è una società chiamata Microquil, penso. Ho la versione 3.3, e quando Java LoadLibrary tenta di caricare quella DLL, fallisce. Non sono sicuro di cosa potrei fare per avere un handle java che carica quella libreria. Deve avere qualcosa a che fare con l'area di memoria a cui può accedere Java, rispetto a ciò che Windows consente a un exe di accedere. L'exe non ha problemi con la libreria SmartHeap, ma Java non mi permetterà di usarlo. Qualche idea o esperienza che si occupa di questo? Ho provato a rimuovere la libreria collegata ricompilando le altre librerie, ma poi le normali chiamate nel codice falliscono che normalmente funzionano.


Ulteriori informazioni che si trovano La funzione che è nel dll che non riesce a caricare in Java è chiamato MemRegisterTask. È da un prodotto chiamato SmartHeap di Microquill. Ecco la documentazione che ho trovato su questa funzione. Penso che questa allocazione di memoria sia ciò che fa sì che java non riesca a caricarlo.

MemRegisterTask inizializza la SmartHeap Library. Sulla maggior parte delle piattaforme, non è necessario chiamare MemRegisterTask perché SmartHeap si inizializza automaticamente quando si effettua la prima chiamata.

SmartHeap mantiene un conteggio dei riferimenti di registrazione per ogni attività o processo. Ogni volta che si chiama MemRegisterTask, questo conteggio dei riferimenti viene incrementato. Se l'ultima chiamata a SmartHeap si verifica prima che l'applicazione sia pronta per la chiusura, è possibile chiamare MemUnregisterTask per terminare SmartHeap. MemUnregisterTask decrementa il conteggio dei riferimenti di registrazione di uno - quando il conteggio è zero, SmartHeap libererà qualsiasi memoria allocata da SmartHeap e lo stato di debug associato all'attività o processo corrente.

+0

Forse pubblicare un codice potrebbe aiutare le persone a vedere di più in questo. Solo un rapido controllo: stai esponendo correttamente la tua DLL a java (es .: http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jniexamp.html#impl) – JScoobyCed

+0

Non sono sicuro di poter pubblicare codice, a causa delle regole di lavoro. Ho analizzato ogni altro errore JNI in cui mi sono imbattuto, quindi sto cercando di capire i concetti per vedere se riesco a capire perché non funziona. Sto esponendo il metodo 1 che il mio programma java utilizza correttamente. Il problema è che la DLL non carica nemmeno in Windows Java, Unix Java funziona bene. – Logan

+0

Sembra che dal messaggio di eccezione sia che i parametri della funzione C++ non siano corretti (inseriti in un tipo diverso dal previsto), oppure il collegamento non viene eseguito correttamente. È possibile che la libreria C++ sia stata compilata con convenzioni di chiamata diverse da quelle fornite da JNI. – Attila

risposta

0

Sembra una convenzione di chiamata o una mancata corrispondenza di dimensioni tipo per me. Ogni compilatore C di Windows ha il proprio insieme di peculiarità e le intestazioni JNI di Windows assumono (una versione recente di) Microsoft Visual C++. Osserva attentamente gli avvertimenti: la perdita di precisione è un brutto segno.

Ad esempio, __int64 è specifico MSVC. È necessario scoprire qual è il nome del tipo intero a 64 bit in Borland C e mapparlo a __int64 prima di includere jni.h.

+0

Il tipo __int64 in Borland è LongLong e sembra già definito. Pensi che pensi che possa impedire il caricamento della mia DLL? Ho cambiato la mia DLL per collegarmi dinamicamente alle altre DLL ora, ma ora il codice fallisce in java, anche se la DLL verrà caricata in java. Lo stesso codice dll funziona correttamente quando viene fatto riferimento a un file exe C++. Deve essere qualcosa con come Java consente l'accesso alla memoria. Non riesco a trovare alcuna buona informazione a riguardo. – Logan

+0

I woudl suggeriscono ancora di rintracciare gli avvertimenti alla causa principale. Inoltre, hai provato a eseguire la tua app con -verbose: jni? Su una JVM diversa da HotSpot? –

+0

Ho provato a correre con -verbose: jni, mi arriva solo al mio codice quindi non riesce ad eseguire i comandi che provengono dalla DLL che devo usare. Sono abbastanza convinto che questo sia un problema di memoria a questo punto. Sembra che questa DLL connessa stia tentando di allocare memoria. Quando java tenta di caricarlo, ottiene un errore di ricorsione e lo stack trabocca.C'è solo un metodo in questa dll che viene effettivamente utilizzato, ma il caricamento lo fa saltare. – Logan

1

Tutto ciò che è utile nel file di registro hs_err .... Di solito c'è uno stack backtrace ecc. che indica qualcosa.

Ha anche provato a eseguire java.exe (con parametri in esecuzione un test che carica il materiale) all'interno del debugger ?

Dalla traccia sopra è possibile vedere che il caricamento sembra funzionare correttamente (l'output della traccia suggerisce che dllentrypoint/dllmain è stato aumentato con l'output di tracciamento da parte dell'utente).

sequenza in carico è la seguente:

  1. carico DLL dipendenti
  2. carico dll stessa
  3. chiamata DllEntryPoint/DllMain w/processo di allegare

quindi questo è già al di là di carico la DLL.

Hai controllato se stai utilizzando i runtime di debug/release da Windows? Il debug può essere in conflitto con il rilascio - Java è rilasciato, probabilmente il tuo exe di esempio era uguale al tuo build di DLL.

+0

Ho praticamente tutte le combinazioni di rilascio e debug che potrei avere. Per fare una versione DLL di rilascio devo solo disattivare il debugging? Ho letto di questo, ma non ero sicuro di come procedere. Sono stato in grado di caricare la mia DLL ora collegando dinamicamente al codice che mi serve, ma il codice che devo eseguire fallisce comunque. Quindi il collegamento dinamico vs statico non ha fatto la differenza. Ho anche letto sull'utilizzo di matrici di byte per la memoria, ma non sono sicuro di come farlo quando sto solo caricando la DLL per iniziare. – Logan

+0

Ho esaminato i file di registro hs_err, ho letto articoli online su come leggerli, ma non è mai stato mostrato come problema. Comunque, cosa interessante, pensavo che il kernelbase.dll non funzionasse, ma ieri ho eseguito il mio codice e ho lanciato un'eccezione di puntatore nullo dal codice C. Ha creato lo stesso log hs_err che ottengo quando eseguo il mio codice. Mentre scrivo questo, mi chiedo se ci sia veramente un nulla da qualche parte. Perché dovrebbe funzionare in Windows come un exe se c'è un valore nullo? Inoltre, perché avrebbe fallito su loadlibrary ... – Logan