在JNA回调函数中处理va_list参数

时间:2012-06-11 09:12:59

标签: java callback jna variadic-functions libgphoto2

如何从Java处理va_list参数,从本机库接收它?

我正在使用一个便于通过回调函数进行日志记录的C库。该库是libghoto2,我正在使用JNA包装器libgphoto2-java来访问其功能。 有关如何完成日志记录的示例,请参阅this C file中的errordumper方法。

我设法使用库gp_log_add_func添加了一个Java编写的回调函数。唯一的问题是,回调函数的签名包含一个我不知道如何处理的va_list参数。

正如C example from earlier所示,va_list args会直接传递到vfprintf。阅读vfprintf manual,很明显它是某种可迭代的数据结构,已经“使用va_start宏进行初始化”,并在使用va_arg进行迭代后使用va_end进行清理是必须的。 但我发现保持JVM崩溃的唯一方法是制作args类型的com.sun.jna.Pointer参数,而String[]Object[]则更合适。

如何从此va_list中获取数据?

N.B。为了访问gp_log_add_func,我添加了一些Java代码:

添加 org.gphoto2.jna.GPhoto2Native

int gp_log_add_func(int logLevel, LogFunc func, Pointer data);

创建 org.gphoto2.jna.LogFunc

public interface LogFunc extends Callback {
    public static final int GP_LOG_ERROR = 0;
    public static final int GP_LOG_VERBOSE = 1;
    public static final int GP_LOG_DEBUG = 2;
    public static final int GP_LOG_DATA = 3;
    public static final int GP_LOG_ALL = GP_LOG_DATA;

    //the args argument is a va_list 
    public void log(int logLevel, String domain, String format, Pointer args, Pointer data);
}

org.gphoto2.jna.LogFunc 的实施和使用:

LogFunc callback = new LogFunc() {
        public void log(int logLevel, String domain, String format, Pointer args, Pointer data) {
            System.out.println("[" + domain + "] " + format);
            System.out.println(args.toString());
        }
};
GPhoto2Native.INSTANCE.gp_log_add_func(LogFunc.GP_LOG_ALL, callback, null);

2 个答案:

答案 0 :(得分:2)

以下是varargs实现的示例,其中提供了有关varargs宏正在做什么的一些提示:

int printf(const char* fmt, ...) {
  va_list argp;

  va_start(argp, fmt); // usually something like: argp = (char *)&fmt - sizeof(void *);

  int arg1 = va_arg(argp, int); // *(int *)argp; argp += sizeof(int);
  void *arg2 = va_arg(argp, void*); // *(void **)argp; argp += sizeof(void *);
  float arg3 = va_arg(argp, float); // *(float *)argp; argp += sizeof(float);

  va_end(argp); // no-op

}

所以它基本上是一堆使用堆栈指针的指针算法。有问题的部分w / r / t JNA是你无法访问堆栈指针,你可能需要在本机级别扩展JNA的回调机制来专门处理可变参数回调以提供堆栈指针。

即使这可能存在问题。从上面的示例中可以看出,实际上需要变量函数签名的最后一个命名参数的地址才能访问可变参数。一般来说这将是非常棘手的。

答案 1 :(得分:0)

也许这与本文的作者无关,但对于像我一样遇到此问题的任何新手,这可能都是有用的。这些问题大多数与日志回调有关,在这种情况下,仅使用C和另一个JNA调用可能对我们有利。

例如,如果需要提供一个具有C签名为log_write(const char* format, va_list args);的回调函数,则可以具有JNA回调,该回调将再次调用C的vsprintf来构造最终字符串。 / p>

public interface CLib extends Library {
  CLib INSTANCE = (CLib) Native.loadLibrary((Platform.isWindows() ? "msvcrt" : "c"), CLib.class);
  int vsprintf(byte[] buffer, String format, Pointer va_list);
} // interface CLib

public static String format(final String format, final Pointer va_args) {
  CLib jnaLib = CLib.INSTANCE;
  byte[] buffer = new byte[2048];
  jnaLib.vsprintf(buffer, format, va_args);
  return new String(buffer);
}