JNA - 使用结构数组作为byref参数

时间:2013-01-16 14:31:07

标签: struct arguments pass-by-reference jna

我知道这个问题的部分内容已经被这里的一些帖子所涵盖,我已经查看了它们并测试了一些,但没有运气。 我有这个本机方法签名,它应该用提供的 CBadgeData结构数组填充结果:

int elc_GetBadges(int nHandle, char* cErr, int* nRecCount, CBadgeData** arr)

CBadgeData结构实现如下:

package test.elcprog;

import java.util.Arrays;
import java.util.List;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;

public class CBadgeData extends Structure{

    public static class ByReference extends CBadgeData implements Structure.ByReference { }

    public int nBadgeID, nTrigger, nExtraData;
    public String cName;

    public CBadgeData(Pointer pointer){
        super(pointer);
    }

    public CBadgeData(){ }

    public String ToString() {
        return nBadgeID + "," + nTrigger + "," + nExtraData + "," + cName;
    }

    @Override
    protected List getFieldOrder() {
        String[] s = new String[]{"nBadgeID","nTrigger","nExtraData","cName"};
        return Arrays.asList(s);
    }
}

我最后一次尝试制作这个论点并称之为方法如下:

CBadgeData.ByReference[] badges = new CBadgeData.ByReference[max_items];
new CBadgeData.ByReference().toArray(badges);
int ret = inst.elc_GetBadges(handle, err, recCount, badges);

失败并出现分段错误。

我的问题是这里应该提供什么样的Java类型作为CBadgeData**调用中原生elc_GetBadges的参数?

编辑-1 -

自己填充数组(有或没有终止空指针)不起作用并导致进一步的Seg崩溃。然后我使用Pointer [] arg作为technomage建议:

Pointer[] pointers = new Pointer[max_items];
for(int i=0; i<max_items; i++){
    pointers[i] = new CBadgeData.ByReference().getPointer();
}
int ret = inst.elc_GetBadges(handle, err, recCount, pointers);

这没有引起任何错误,但似乎没有对返回的struct进行任何更改,在这种情况下应该包含4个项目:

int bid = new CBadgeData(pointers[i]).nBadgeID; // this returns null for all items

在struct上使用显式read()/ write()导致Seg再次崩溃(在读取时): 知道我还在这里缺少什么吗?


编辑-2 -

有趣的是 - 在调用本机方法后直接使用Memory.get可以获得正确的结果:

Memory m= (Memory)pointers[0];
System.out.println("1st int: "+m.getInt(0)); // this gets 24289 which is 5ee1
System.out.println("2nd int: "+m.getInt(4)); // this gets 3
System.out.println("3rd int: "+m.getInt(8)); // this gets 255
System.out.println("String: "+m.getString(12)); // this gets "Badge[5EE1]" as supposed

read()仍然崩溃。有什么想法吗?

1 个答案:

答案 0 :(得分:1)

我推断CBadgeData **输入是一个指向CBadgeData的指针数组。

因此,Structure.ByReference标记是正确的。

Structure.toArray()在这里可能,或者至少不是必需的(它在内存中分配一个连续的结构块)。您可以使用CBadgeData.ByReference实例填充数组。

也许你的被调用者期望在数组末尾有一个NULL指针?我没有看到被调用者的数组长度的另一个指示符。

CBadgeData.ByReference[] badges = new CBadgeData.ByReference[max_items+1];
for (int i=0;i < badges.length-1;i++) {
    badges[i] = new CBadgeData.ByReference();
}
badges[badges.length-1] = null;

非常确定有效。如果由于某种原因有错误处理Structure.ByReference[],我知道Pointer[]是可靠的并且会做同样的事情。

修改

如果您使用Pointer[]代替Structure.ByReference[](如果Structure.ByReference[]不起作用,请将错误发布到项目网站),您必须先手动调用Structure.write/read /在您的本机函数调用之后,因为JNA不会知道指针引用需要与本机内存同步的结构。但是,我敢打赌,使用Structure.ByReference[]时崩溃的原因只是JNA在调用后自动调用Structure.read()并触发了您在显式调用它时看到的相同错误。

如果您在读取时遇到段错误,则可能意味着您的结构字段未正确对齐或定义,或者(不太可能)您具有无法正确读取的损坏数据。要进行诊断,请设置jna.dump_memory=true并在调用Structure.write()后打印出结构,以查看结构内容是否按预期显示。如果可能的话,在这里发布你的结构的原生和JNA形式也是有帮助的。