我在使用 JNA 将回调函数从 C-DLL 映射/转换为 Java 时遇到问题。
在C头文件中编写了以下回调函数:
// ! callback function header whenever a data report is received from a device
typedef void (FR_callback_func)(Data_t frame);
上述Data_t
的结构如下:
// ! Carries information about one signal.
typedef struct
{
unsigned char index;
int isval;
unsigned short val;
int arr_Length;
unsigned char array[8];
} Data_t ;
调用Data_t
结构的函数:
int getData(int val,Data_t *data);
现在我翻译成我的JAVA代码如下:
public interface device extends Library
{
public interface FR_callback_func extends Callback
{
void invoke(Data_t signal);
}
public class Data_t extends Structure implements com.sun.jna.Structure.ByReference
{
public static class ByReference extends Data_t implements Structure.ByReference { }
public byte index;
public int isval;
public short val;
public int arr_Length;
public byte[] array = new byte[8];
@Override
protected java.util.List<java.lang.String> getFieldOrder()
{
return Arrays.asList(new String[] {"index","isval","val","arr_Length","array"});
}
}
public int getData (int val,Data_t.ByReference data);
}
然后我尝试在我的主函数中使用它,如下所示:
public static void main(String[] args) throws IOException
{
Data_t .ByReference data_t = new Data_t .ByReference();
int data = 0;
int val = 0;
device h = (device) Native.load("Library", device.class);
data = h.getData (val, data_t);
}
我的问题是我是否正确翻译了上述 C 代码?特别是回调函数?因为C代码无法被操纵。因此我必须将提供的 C-DLL 代码翻译成 JAVA。
我们将非常感谢您的建议。
最佳答案
粗略地写下了上面您仍然缺少的所需部分:
- C 示例库
// gcc -c -Wall -Werror -fPIC foo.c
// gcc -shared -o libfoo.so foo.o
#include <stdio.h>
typedef struct {
int x;
int y;
} Foo;
typedef Foo* (*FooCallback)(int, int);
void printFoo(Foo *foo) {
printf("foo: %p x: %d y: %d\n", foo, foo->x, foo->y);
}
FooCallback callback = NULL;
void setCallback(FooCallback cb) {
callback = cb;
}
void runCallback() {
if (callback) {
Foo *foo = callback(123, 456);
printf("foo from callback: %p x: %d y: %d\n", foo, foo->x, foo->y);
} else {
printf("callback not set!\n");
}
}
- Java 示例代码
// javac -classpath .:jna.jar Foo.java
// java -classpath .:jna.jar Foo
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Structure;
import com.sun.jna.Callback;
public class Foo {
public interface CFoo extends Library {
public class SFoo extends Structure {
public static class ByReference extends SFoo implements Structure.ByReference { }
public static class ByValue extends SFoo implements Structure.ByValue { }
public int x;
public int y;
}
void printFoo(SFoo foo);
public interface FooCallback extends Callback {
SFoo invoke(int x, int y);
}
void setCallback(FooCallback cb);
void runCallback();
}
public static void main(String[] args) {
CFoo cfoo = (CFoo) Native.loadLibrary("libfoo.so", CFoo.class);
// test construction of SFoo
CFoo.SFoo foo = new CFoo.SFoo();
foo.x = 23;
foo.y = 42;
// test auto ByReference
System.out.println("foo pointer: " + foo.getPointer());
cfoo.printFoo(foo);
// callback test
// no callback set yet
cfoo.runCallback();
// set callback
// declare return value outside to "survive" callback scope
// (this is needed for ByReference return values)
CFoo.SFoo foo2 = new CFoo.SFoo();
System.out.println("foo2 pointer: " + foo2.getPointer());
CFoo.FooCallback cb = new CFoo.FooCallback() {
public CFoo.SFoo invoke(int x, int y) {
System.out.println("values from C x: " + x + " y: " + y);
// Is it safe to initialize ByValue inside the callback?
// CFoo.SFoo.ByValue foo2 = new CFoo.SFoo.ByValue();
foo2.x = x;
foo2.y = y;
foo2.write(); // explicitly write to memory needed here
return foo2;
}
};
cfoo.setCallback(cb);
// rerun with set callback
cfoo.runCallback();
// hack to avoid gc for cb and foo2
assert cb != null : "Oops";
assert foo2 != null : "Oops";
}
}
输出
foo pointer: auto-allocated@0x7f5588284050 (8 bytes)
foo: 0x7f5588284050 x: 23 y: 42
callback not set!
foo2 pointer: auto-allocated@0x7f55882cfa50 (8 bytes)
values from C x: 123 y: 456
foo from callback: 0x7f55882cfa50 x: 123 y: 456
请注意,如果有回调,GC 需要特别小心。具有非原始数据类型。因此,我在示例中添加了一个回调,将结构本身返回给 C (foo2)。由于该对象是通过引用返回的,因此它必须在回调后处理它的 C 代码中幸存下来。我不知道 JNA 是否在任何 GC 交互发生之前正确移动由值返回的结构。您必须在 JNA 代码中查找该内容。
希望这有帮助。
编辑:旁注 - 我发现 JNI 更容易与 C 结合使用,主要是为了更接近内存(对 C 更友好)。尽管最初需要做更多的工作(对于 C 中的包装器),但后来会获得更好的性能。
关于java - C-DLL 中的回调函数转换为 java JNA,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59412267/