2012-04-05 41 views
16

我創建了一個C++模塊來構建共享庫文件,然後使用JNI從Java調用它。我的dll代碼從exe文件運行,但無法從Java加載loadLibrary

我有2個環境,Windows和Unix,我有一個C++可執行程序和一個Java程序,我只是爲每個環境重新編譯。

  • 當我在Unix中編譯我的tester.exe程序並使用我的庫(.so)中的方法 運行它時,它工作正常。
  • 當我在Unix中編譯我的Java程序並使用 Java的loadLibrary加載我的庫(.so)時,它工作正常。
  • 當我在Windows中編譯我的tester.exe程序並使用我的庫(.dll)中的 方法運行它時,它工作正常。就像unix 版本一樣。

  • 當我在Windows中編譯我的Java程序並使用Java的loadLibrary加載我的庫(.dll)時,它失敗。它表示嘗試訪問 無效地址。

我想不通,爲什麼在Windows上運行時也不會與Java調用LoadLibrary工作,但它的工作原理其他地方使用相同的代碼。如果我延遲加載我的庫使用的依賴DLL,那麼我的庫會在Java中加載,但不起作用。我知道有些特定的代碼會導致Java加載我的庫的問題,但我無法弄清楚爲什麼我的C++ EXE在相同的方法和庫中沒有問題。


我的DLL有1個暴露的方法,它從一些現有的庫調用4個方法。如果我評論這4種方法,那麼我的dll加載Java的罰款。我知道這是從我的DLL鏈接到一個庫中的這些方法。 Java看到依賴庫有什麼不同嗎?我已經嘗試加載依賴庫首先,但我加載的DLL文件之一導致遞歸錯誤和堆棧溢出。

任何人都知道一個DLL的方式,導致從遞歸錯誤堆棧溢出?我需要它的方法,但我不能用java loadLibrary加載它。


下面是有關涉及的文件和實際的錯誤消息的更多詳細信息。 我添加了一個DllMain到我的inital dll文件,以查看什麼時候加載。如果我編譯相同的程序(my_plain_dll_to_call_JNI_DLL)作爲exe文件,一切工作正常。如果我編譯它並從我的Java程序加載它會發生。

  • myJavaProgram,只需調用System.loadLibrary()加載一個基本的.dll文件 調用在我的其他DLL的方法包含JNI代碼。
  • my_plain_dll_to_call_JNI_DLL是我通過將它鏈接到我的 dll庫文件創建的一個dll,用於測試依賴關係。它只是從調用我需要的本機代碼的其他dll調用方法 。
  • my_JNI_DLL.ll是一個dll文件,與我需要從JNI訪問的現有C++編程 庫鏈接。它包含對現有源代碼庫中的 方法的直接調用。

我寫的顯示文本每行左邊顯示的執行是在哪一層的文件名。

 

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 
 

更新 我已經縮小的問題一個內存管理庫,從我的程序使用的另一個dll鏈接。它使用的dll是sh33w32.dll,它叫做SmartHeap,由我認爲是由一家名爲Microquil的公司提供。我有版本3.3,並且當Java LoadLibrary嘗試加載該dll時,它失敗。我不知道我能做些什麼來讓Java處理加載該庫。它必須與Java可以訪問的內存區域有關,而與Windows允許exe訪問的內存區域有關。這個exe文件對於SmartHeap庫沒有問題,但是Java不允許我使用它。任何想法或經驗處理這個?我試圖通過重新編譯其他庫來移除鏈接的庫,但隨後代碼中的正常調用失敗,通常無法正常工作。


附加資料找到 即在失敗在Java加載DLL被稱爲MemRegisterTask功能。它來自Microquill的名爲SmartHeap的產品。這裏是我找到關於這個功能的文檔。我認爲這種內存分配是導致java無法加載它的原因。

MemRegisterTask初始化SmartHeap庫。在大多數平臺上,您不需要調用MemRegisterTask,因爲SmartHeap會在您進行第一次調用時自行初始化。

SmartHeap爲每個任務或進程維護註冊引用計數。每次調用MemRegisterTask時,該引用計數都會增加。如果您最後一次調用SmartHeap發生在應用程序準備終止之前,您可以調用MemUnregisterTask來終止SmartHeap。 MemUnregisterTask將註冊引用計數遞減1 - 當計數爲零時,SmartHeap將釋放與當前任務或進程關聯的任何SmartHeap分配的內存和調試狀態。

+0

也許發佈一些代碼會幫助人們看到更多。只是一個快速檢查:你是正確地暴露你的DLL到Java(例如:http://java.sun.com/developer/onlineTraining/Programming/JDCBook/jniexamp.html#impl) – JScoobyCed 2012-04-06 15:11:22

+0

我不知道我可以發佈代碼,因爲工作規則。我已經解決了我遇到的所有其他JNI錯誤,所以我試圖瞭解這些概念,以瞭解是否可以看到它失敗的原因。我暴露了我的java程序正確使用的1方法。問題是DLL甚至不能在Windows Java中加載,Unix Java可以正常工作。 – Logan 2012-04-06 15:30:59

+0

從異常消息看來,C++函數的參數不正確(編組爲不同於預期的類型),或者鏈接不正確。有可能C++庫是用不同於JNI提供/假定的調用約定編譯的。 – Attila 2012-04-06 18:08:02

回答

0

看起來像一個調用約定或類型大小與我不匹配。每個Windows C編譯器都有自己的一套特性,並且Windows JNI頭文件假定(最新版本)Microsoft Visual C++。仔細觀察警告 - 精確度的損失是不好的跡象。例如,__int64是MSVC特定的。您需要找出Borland C中64位整數類型的名稱,並將其映射到__int64,然後才包含jni.h

+0

Borland中的__int64類型是LongLong,看起來它已經定義好了。是否認爲會阻止我的DLL加載?我改變了我的DLL現在動態鏈接到其他DLL,但現在代碼在Java中失敗,儘管DLL將加載到Java中。當從C++ exe文件引用時,相同的dll代碼仍然可以正常工作。它必須與Java如何訪問內存有關。我無法找到任何有關這方面的好消息。 – Logan 2012-04-24 01:35:42

+0

我還是建議跟蹤警告的根本原因。此外,您是否嘗試使用-verbose:jni運行您的應用程序?在HotSpot以外的JVM上? – 2012-04-25 09:30:56

+0

我試着用-verbose:jni運行,它只是得到我的代碼,然後執行從我需要使用的DLL的命令失敗。我相當確信這是一個記憶問題。這聽起來像是這個鏈接的DLL試圖分配內存。當java嘗試加載它時,它會得到一個遞歸錯誤並且堆棧溢出。在這個dll中只有1個方法可以使用,但是加載它會吹動堆棧。 – Logan 2012-04-25 23:59:55

1

任何有用的hs_err ...日誌文件。通常有一個堆棧回溯等 指出一些東西。

還嘗試運行調試器內部的 內的java.exe(帶參數運行測試加載的東西)?

從上面的跟蹤可以看到加載似乎工作正常(跟蹤輸出建議 dllentrypoint/dllmain已增加W /跟蹤輸出由你)。

順序裝載如下:

  1. 負載相關的DLL
  2. 加載DLL本身
  3. 調用DLL入口/ DllMain的W /進程附加

所以這已經超出負荷該DLL。

您是否檢查過您是否使用Windows的調試/發佈運行時?調試可能與發佈衝突 - Java是釋放,您的示例exe可能與您的dll構建相同。

+0

我幾乎擁有我可能擁有的各種發佈和調試組合。要發佈版本的DLL,我只需要關閉調試?我已經閱讀了這篇文章,但並不確定如何去做。我已經能夠通過動態鏈接到我需要的代碼來加載我的dll,但是我需要執行的代碼仍然失敗。所以動態vs靜態鏈接沒有什麼區別。我還閱讀了有關使用字節數組的內存,但不知道如何做到這一點,當我只是加載DLL開始。 – Logan 2012-04-26 00:03:03

+0

我已經通過hs_err日誌文件,在線閱讀關於如何閱讀這些文章的文章,但是沒有任何文章顯示爲問題。雖然有趣的事情,我認爲kernelbase.dll失敗,但昨天我跑我的代碼,並從C代碼拋出空指針異常。它創建了我運行我的代碼時獲得的相同hs_err日誌。當我輸入這個,它讓我懷疑是否真的有一個空的地方。爲什麼它會在Windows中作爲exe文件工作,但是如果有空值?另外爲什麼它會在加載庫上失敗...... – Logan 2012-04-26 00:06:00