1. 程式人生 > >JNA的使用方法簡介(struct和union)

JNA的使用方法簡介(struct和union)

最近因為專案開發需要,用了到JNA的相關技術。下面就使用中的一些體會進行一下簡單的總結。

基本知識連結

遇到的主要問題

1.結構體內嵌結構體陣列的問題

按照網上的教程,對於巢狀的結構體,需要定義為 xxx.ByValue;但實際驗證過程中,定義成xxx.ByValue方式時,會出現java結構體和c語言結構體記憶體大小不一致的情況;只需要定義成xxx即可。如下所示

public class RwResult extends Structure
{
    public int tagNum;
    public TagResult[] tagResult = new
TagResult[8]; { for(int i = 0; i < 68; i++) { tagResult[i] = new TagResult(); } } public static class ByValue extends RwResult implements Structure.ByValue{ } public static class ByReference extends RwResult implements Structure.ByReference{ } }
2.對於聯合體的使用

下面是JNA官網上的一段原話

==Represents a native union. When writing to native memory, the field corresponding to the type passed to setType(java.lang.Class) will be written to native memory. Upon reading from native memory, Structure, String, or WString fields will not be initialized unless they are the current field as identified by a call to setType(java.lang.Class). The current field is always unset by default to avoid accidentally attempting to read a field that is not valid. In the case of a String, for instance, an invalid pointer may result in a memory fault when attempting to initialize the String==

也就是說,在使用聯合體的成員時,需要提前設定將要使用的成員型別,然後再使用。否則,雖然能夠成功設定值,並且編譯時沒有報錯,但是沒有辦法傳真正入到記憶體中。
例如:

        rwPara.rwInfo.setType(ReadOpInfo.class);
        rwPara.rwInfo.readOpInf.opId = 0x10;
        rwPara.rwInfo.readOpInf.mb = 0x11;
        rwPara.rwInfo.readOpInf.offset = 0x12;
        rwPara.rwInfo.readOpInf.len = 0x13;

上面程式碼中,readOpInfo是一個聯合體,在使用其中的某一個型別ReadOpInfo時,需要提前設定該聯合體型別,然後再給聯合體內的成員賦值。

上面的聯合體是一個傳入引數,也就是說我提前知道我要使用哪個型別,所以可以先設定。那麼問題來了,如果我的聯合體是一個傳出引數,不知道傳出來的是什麼型別,怎麼辦?
比如:

        RwResult.ByReference rwresult = new RwResult.ByReference();
        getResult(0,0,rwresult);//先傳入一塊記憶體
        rwresult.rwOpResult.SetType(ReadCustomResult.class);//然後設定聯合體型別
        tmp = rwresult.rwOpResult.xxx //訪問某個ReadCustomResult成員

很遺憾,jna不支援這種方式,我們並不能將想要結果取出來。
所以,我們只能用下面的方式

        RwResult.ByReference rwresult = new RwResult.ByReference();
        rwresult.rwOpResult.SetType(ReadCustomResult.class);//先設定聯合體型別
        getResult(0,0,rwresult);//傳入一塊記憶體

        tmp = rwresult.rwOpResult.xxx //訪問某個ReadCustomResult成員

也就是說,再不確定傳出引數的型別時,我們沒辦法正確得到想要的資料。這讓人很困惑,在C語言中這是一個很普通的應用場景,JNA卻沒有辦法解決。

也可能是我那些地方沒有理解,沒有找到正確的使用方法,或者JNA有其他的替代方案。

還好,專案中通過其他的規避方式,能夠提前知道返回的聯合體的型別,使用JNA的聯合體的不足給解決了。不過,後來大家又提出一個方案,能不能將傳入傳出引數用第三方跨平臺跨語言工具進行序列化和反序列化,這後續會進行下預研。