JNA 调用c++函数的出错,java能加载库,但是找不到c++写的函数

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JNA 调用c++函数的出错,java能加载库,但是找不到c++写的函数相关的知识,希望对你有一定的参考价值。

void func(char*);这是c++的函数;
jna 接口中对应的函数为void func(string a);
能加载so文件,但是找不到这个函数,报错如下:
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'func': /lib/libfunc.so: undefined symbol: func
c++源码的include和extern c这些地方是不是有些要注意的关键细节?
系统:ubuntu 14.04

调用C++编译的代码能调用到库但是找不到函数,应该是因为C++编译器会破坏函数的名称。

对此,一般网上能查到两种方法解决:

    用extern C ,功能是将部分代码用C编译器进行编译,因为 C编译器不会改变函数的函数名,Java中就可以用代码中的函数名调用了。

    用def模板

注1:这里说的方法你需要自己去查一下,别人的文章会写得比较清楚,看完整的文章能学到更多。

注2: 对于C和C++编译器对函数名的影响,这里和你说一下如何查看:你想看看C++编译器编译的DLL的方法名可以用depends反编译程序,把dll拖进depends程序窗口,左边会有依赖树,点击最顶端你的“库名.dll”,右边就能看到dll中函数被C++编译之后的名字。如下图:

    用C编译的DLL:

2. 用C++编译的DLL:

参考技术A import com.sun.jna.Library;
import com.sun.jna.Native;
public class TestSo
public interface LgetLib extends Library
// 调用linux下面的so文件,注意,这里只要写test就可以了,不要写libtest,也不要加后缀
LgetLib INSTANCE = (LgetLib) Native.loadLibrary("test",LgetLib.class);
int add(int a,int b);

public int add(int a,int b)
return LgetLib.INSTANCE.add(a,b);

public static void main(String[] args)
TestSo ts = new TestSo();
int c = ts.add(10,20);
System.out.println("10+20="+c);

从 JNA 调用 libc 函数

【中文标题】从 JNA 调用 libc 函数【英文标题】:Call libc function from JNA 【发布时间】:2015-01-26 20:24:30 【问题描述】:

我从 Java 到 JNA 使用了一个 C 库,但一个函数没有正确刷新(因为输出在程序结束时同时出现)。我试过 Java 端 System.out.flush(); 没有运气。

简而言之,我想从 Java 调用 C fflush(stdout)。 JNA 已经存在(因此如果没有额外的库会更喜欢)并且没有 C 来编写。

我知道 this question 中的 JNA 库映射,但这对我来说似乎有点过头了。

【问题讨论】:

【参考方案1】:

JNA 库的包装方式代码实际上并没有那么重(至少对于刷新所有行为)。

protected interface CLibrary extends Library

  static CLibrary clib = (CLibrary) Native.loadLibrary ("c", CLibrary.class);
  int fflush (Pointer stream);


/* ... */
  CLibrary.clib.fflush (null);

JNA 还提供 后期绑定 方法,这些 oneliners 会做你想做的事

NativeLibrary.getInstance ("c").getFunction ("fflush").invokeInt (new Object[]0);
// even shorter
Function.getFunction ("c", "fflush").invokeInt (new Object[]0);

当您想将刷新限制为stdout 时,就会出现乏味的部分。您必须处理vendor-specific code(stdout 定义为扩展为数组Amtel avr-libc、函数调用Microsoft msvcrt 或GNU libc 中的指针的宏)。

对于 libc,您可以使用(两行代码便于阅读)

Pointer stdout = NativeLibrary.getInstance ("c").getGlobalVariableAddress ("stdout").getPointer (0);
Function.getFunction ("c", "fflush").invokeInt (new Object[]stdout);

【讨论】:

【参考方案2】:

为 Win32 / Win64 用户添加此答案,补充 FabienAndre 的 GNU libc。

通过 jna 选择性地刷新调用系统的 c 库的 fflush 方法的 stdout 流既困难又麻烦。正如 FabienAndre 已经提到的,很难掌握 stdout 宏定义。对于 msvcrt(Win32 / Win64 C 库),它是通过对__iob_func() 的函数调用来定义的;后者返回指向FILE 结构数组的指针。索引 0 是标准输入,索引 1 是标准输出,索引 2 是标准错误。所以对于flush stdout你甚至需要知道FILE结构的大小,当然Win32和Win64是不同的......

以下示例在 Win64 下进行了测试,但应该可以在 Win32 下运行。它的灵感来自线程 JNA solutions to catch stdout/stderr of DLL。

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
import com.sun.jna.Pointer;

public class JnaTest 

  public interface CLibrary extends Library 
    CLibrary INSTANCE = (CLibrary) Native.loadLibrary("msvcrt" , CLibrary.class);

    Pointer __iob_func();
    void printf(String format, Object... args);
    int fflush (Pointer stream);
  

  public static void main(String[] args) 
    int sizeOfFileStructure = Platform.is64Bit() ? 48 : 32;
    Pointer stdout = CLibrary.INSTANCE.__iob_func().share(sizeOfFileStructure);
    CLibrary.INSTANCE.printf("Hello, World\n");
    CLibrary.INSTANCE.fflush(stdout);
  

【讨论】:

以上是关于JNA 调用c++函数的出错,java能加载库,但是找不到c++写的函数的主要内容,如果未能解决你的问题,请参考以下文章

JNA调用库文件

使用JNA,让java调用原生代码

java通过JNA调用动态库

java通过JNA调用动态库

使用 JNA 从 Java 调用 C++ dll 方法并避免方法名称修改

JNA的用法