使用JNA将Java类传递给void*参数

Iai*_*oat 10 java pointers jna

我在C中有一个函数,我试图用JNA调用Java :

int myCfunc(void *s, int *ls);
Run Code Online (Sandbox Code Playgroud)

根据JNA文档,void*需要com.sun.jna.Pointer传递给函数.在带有JNA的java中我相信上面的函数将包装如下:

public interface myWrapper extends Library{ 
    public int myCfunc(Pointer s, IntByReference ls);
}
Run Code Online (Sandbox Code Playgroud)

需要链接到指针并传入参数的对象s将是实现JNA结构的类,例如:

public class myClass extends Structure{
    public int x;
    public int y;
    public int z;
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,该参数ls是一个整数,表示以字节为单位的类的长度.Java没有sizeof函数,因此增加了额外的复杂性.我遇到的另一个主要问题是确保我正确地将对象的内容传递给本机内存并返回.

我的代码类似于以下内容:

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import com.sun.jna.ptr.IntByReference;

public void foo(){
    myWrapper wrapper = (myWrapper) Native.loadLibrary("SomeDllWithLegacyCode", myWrapper.class);

    myClass myObj = new myClass();
    myObj.x = 1;
    myObj.y = 2;
    Pointer myPointer = myObj.getPointer();

    int size = Native.getNativeSize(myClass.class);
    IntByReference len = new IntByReference(size);

    myObj.write(); //is this required to write the values in myObj into the native memory??
    wrapper.myCfunc(myPointer, len);
    myObj.read();  //does this read in the native memory and push the values into myObj??

    myPointer.clear(size); //is this required to clear the memory and release the Pointer to the GC??
}
Run Code Online (Sandbox Code Playgroud)

我收到一个错误,传递的数据大小比 C函数预期的大.

上面的代码大致遵循了与处理类似问题但在C#中的问题的答案中提供的相同类型的步骤.我已经尝试并测试它在C#中有效.

我的问题类似于Stackoverflow上的另一个问题,但它处理指向IntByReference的指针而不是指向类的指针.

Cer*_*ber 6

首先,JNA自动处理它自己的内存分配,这意味着跟随行是无用的(并且可能损坏内存堆栈):

myPointer.clear(size); //is this required to clear the memory and release the Pointer to the GC??
Run Code Online (Sandbox Code Playgroud)

接下来它还会自动处理本机指针类型,这意味着下面的两行在您的情况下是等效的:

public int myCfunc(Pointer s, IntByReference ls);
public int myCfunc(myClass s, IntByReference ls);
Run Code Online (Sandbox Code Playgroud)

因而JNA会做 myObj.write();read你.

以下是100%正确,但我建议您len.getValue()在调用之前和之后进行记录myCfunc(可以给出3*4 = 12; 3个4字节的int):

int size = Native.getNativeSize(myClass.class);
IntByReference len = new IntByReference(size);
Run Code Online (Sandbox Code Playgroud)

如果所有这些都是正确的,那么您的结构原型可能会出错.

根据我的经验,这主要是由于过时的C头文件或过时的库:

  • 您使用的是头版本2.0,它表明struct的值是int,但是您对库v1.0的链接采用了字节结构
  • 你正在使用头版本2.0,它表明struct的值是int,但是你对库v1.9的链接需要两个整数和一个字节

最后你的代码应如下所示:

public void foo(){
    myWrapper wrapper = (myWrapper) Native.loadLibrary("SomeDllWithLegacyCode", myWrapper.class);

    myClass myObj = new myClass();
    myObj.x = 1;
    myObj.y = 2;

    int size = Native.getNativeSize(myClass.class);
    IntByReference len = new IntByReference(size);

    //log len.getValue
    wrapper.myCfunc(myObj, len);
    //log len.getValue
}
Run Code Online (Sandbox Code Playgroud)

您还可以尝试自愿降低len的值以进行调试,例如:

IntByReference len = new IntByReference(size-1);
IntByReference len = new IntByReference(size-2);
IntByReference len = new IntByReference(size-3);
//...
IntByReference len = new IntByReference(size-11);
Run Code Online (Sandbox Code Playgroud)

这不会做你想要的,但至少它应该给你正确的"最大len"