2013-02-20 59 views
2

我目前正在開發一個項目,該項目需要開發一個本機DLL(使用C++)以供Java應用程序訪問。我選擇了JNA進行橋接工作,並且遇到了將正確的int值從Java傳遞到C++函數的問題。如何使用JNA將int作爲Java的C++函數參數傳遞

簡單地說,我有一個接受一個int值在C參數++的函數: (代碼被剝離和方法被重命名爲保持機密性)

JAVALINK_EXPORT SomeStructure WINAPI GetSomeStructureFromIndex(int index) { 
    std::string debugMsg("Received index of "); 
    debugMsg.append(toString(index)); 
    OutputDebugString(debugMsg.c_str()); 
    SomeStructure result = defaultStructure; 
    if (index >= 0 && index < structListSize) 
     result = structList[index]; 

    return result; 
} 

toString是轉換的值的簡單方法的任何數據類型爲std::string使用std::stringstream。實現如下:

template <class T> 
inline std::string toString (const T& t) { 
    std::stringstream ss; 
    ss << t; 
    return ss.str(); 
} 

SomeStructure從我使用的代碼的實際結構改名。 structList是一組SomeStructurestructListSizestructList都是共享內存中的全局變量。

這是在Java接口的DLL方法簽名:

SomeStructure.ByValue GetSomeStructureFromIndex(int index); 

這是我使用的方法在Java中測試用例:

SomeStructure.ByValue received = library.GetSomeStructureFromIndex(1); 

library是一個實例使用Native.loadLibrary生成DLL文件的接口(StdCallLibrary的子類)。當上面的代碼中的Java執行時,我得到類似以下的輸出在我的Windows調試輸出:然後

Received index of 86701080 

(該程序將在遇到訪問衝突錯誤,如果我省略了檢查index與在從陣列中獲得結構之前的行if (index >= 0 && index < structListSize)

86701080可以是任意的值。我意識到它根據導出函數的簽名而改變。我在這裏錯過了什麼嗎?功能適當地接收的1期望值已經函數簽名被void PrintIndex(int index)

EDIT(0):我已經修改了示例代碼的實際代碼更緊密地匹配。編輯(1):根據@ technomage的指針我開始使用ByValue所有方法簽名和變量收集返回的結構。編輯(2):與C++中的SomeStructure結構相比,Java類SomeStructure有一個額外的變量和一個Java方法。我目前正在測試這是否是造成這種差異的原因。

問題迎刃而解

@technomage解釋說,C++函數來解釋它的參數和因爲預計到,所使用的結構的大小作爲返回類型(以及那些用作函數參數返回值)不應該與Java相對應。這可以在SomeStructure的情況下用C++ sizeof(SomeStructure)和Java用SomeStructure.size()來檢查。

基本上,發生的事情是SomeStructure結構的大小與其Java表示不同。 SomeStructure包含如圖所示在下面的代碼一個固定長度的數組:

#define MAX_LIST_SIZE 256 
typedef struct { 
    int list[MAX_LIST_SIZE]; 
    int length; 
} SomeStructure; 

然而,Java表示沒有指定固定長度數組的大小。 list已初始化爲包含單個值0

package model; 

import com.sun.jna.Structure; 

public class SomeStructure extends Structure { 
    public static class ByValue extends SomeStructure implements Structure.ByValue { } 
    public int[] list = {0}; 
    public int length = 0; 
} 

我用,而不是下面的替換有故障的初始化語句解決了這個問題:

private static final int MAX_LIST_SIZE = 256; 
public int[] list = new int[MAX_LIST_SIZE]; 

注意:整型常量MAX_LIST_SIZE聲明private以保持Java的唯一。

做出所有這些修改後,我的代碼工作正常,不再遇到訪問衝突。

+0

什麼是toString? – Leonidos 2013-02-20 15:30:47

+1

你試過類似 if(index == 1) OutputDebugString(_T(「passed」)); else OutputDebugString(_T(「failed」)); 在這種情況下檢查toString是否正常工作? – 2013-02-20 15:32:07

+0

我修改了這個問題來解釋錯誤不會發生在'toString'中。 – derfshaya 2013-02-20 16:03:58

回答

1

'WINAPI'意味着stdcall調用約定,但您必須查看您的本地宏定義才能確定。如果是這樣,您需要StdCallLibrary而不是Library。這可能會影響您收到的index參數。

您也在複製您的結構(通過值語義)而不是指向它的指針。當按值傳遞結構或按值返回結構時,您需要tell JNA that you're doing so

編輯

確保SomeStructure.size()匹配的本地sizeof(SomeStructure)。通常,通過值返回的結構被實現,使得調用者在堆棧上分配內存,並將隱式指針傳遞給被調用者,然後寫入該內存。如果調用者和被調用者對該內存的大小不一致,則可能會影響堆棧上的其他內容(例如參數和返回值)。您還可以在函數中添加幾個參數並打印它們的值(如十六進制)以近似地顯示堆棧中的內容。如果您傳入可識別的參數(例如0x12345678),則通常會清楚以什麼方向推動或拉動堆棧。

+0

感謝您的指點。然而,同樣的問題仍然發生(具有完全相同的數字'86701080') 根據[windows.h']的MSDN頁面(http://msdn.microsoft.com/zh-cn/ us/library/windows/desktop/aa383751(v = vs.85).aspx)'WINAPI'確實是'__stdcall'。自從我收到您的建議以來,我已經轉向使用'StdCallLibrary'。 對我所有的方法簽名也使用了'ByValue'。編輯答案反映了這一點。 – derfshaya 2013-02-21 04:13:06

+0

我無意中在Java中實現'SomeStructure'類時添加了額外的變量和方法。這些添加可能是您編輯中提到的不一致和不一致的原因嗎? – derfshaya 2013-02-21 12:53:36

+0

當然。添加到「SomeStructure」的任何公共字段都將有助於其相應的原始大小。使它們保護,默認(包)或私人訪問,以保持它們僅限於java。 – technomage 2013-02-21 13:00:06

相關問題