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

Posted

技术标签:

【中文标题】使用 JNA 从 Java 调用 C++ dll 方法并避免方法名称修改【英文标题】:Calling a C++ dll method from Java using JNA and avoiding Method Name Mangling 【发布时间】:2013-04-12 07:33:25 【问题描述】:

我一直在查看 *** 上有关如何解决方法名称错误的链接,但没有找到任何具有实时示例的解决方案。

场景-C++ Ex.dll 文件由客户端提供。我需要访问 Ex.dll 并通过 Java 调用相同的方法。

限制 - 无法修改 Ex.dll,我只能访问相同的。

面临的问题 - 当我通过 JNA 访问 Ex.dll 时出现以下异常

Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'getCPUSpeed': The specified procedure could not be found.
at com.sun.jna.Function.<init>(Function.java:134)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:336)
at com.sun.jna.NativeLibrary.getFunction(NativeLibrary.java:316)
at com.sun.jna.Library$Handler.invoke(Library.java:203)
at $Proxy0.getCPUSpeed(Unknown Source)
at cpp.java.JnaTest.main(JnaTest.java:16)

google了很多,发现是因为方法名Mangling,但还是找不到任何好的解决方案示例代码。

这是我使用的代码-

import com.sun.jna.Native;
class JnaTest


public static void main(String args[])

    try 
    
        JnaInterface jInterface = (JnaInterface) Native.loadLibrary("Ex", JnaInterface.class);

        System.out.println("Calling C++ DLL method");
        System.out.println("========================");
        System.out.println("getCPUSpeed() -- "+jInterface.getCPUSpeed());

     catch (Exception e) 
        e.printStackTrace();
    





package cpp.java;
import com.sun.jna.Library;
public interface JnaInterface extends Library  
public int getCPUSpeed();
 

更新 1:**************************************

下面提到的是我通过依赖walker浏览DBMM.dll时得到的实际功能-

DBMM DLL 函数-

??0cDbmmInterfaceCache@@QAE@ABV0@@Z

??0cDbmmInterfaceCache@@QAE@XZ

??0cDbmmInterfaceControl@@QAE@ABV0@@Z

??0cDbmmInterfaceControl@@QAE@XZ

??0cDbmmInterfaceEcon@@QAE@ABV0@@Z

??0cDbmmInterfaceEcon@@QAE@XZ

??0cDbmmInterfaceKnob@@QAE@XZ

??0cDbmmInterfaceOutput@@QAE@ABV0@@Z

??0cDbmmInterfaceOutput@@QAE@H@Z

??0cDbmmInterfacePoolLoan@@QAE@ABV0@@Z

??0cDbmmInterfacePoolLoan@@QAE@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z

??0cDbmmMacroEcon@@QAE@ABV0@@Z

??0cDbmmMacroEcon@@QAE@ABVcDbmmInterfaceEcon@@_N@Z

??0cDbmmMtgBasisConstSpreadModel@@IAE@XZ

??0cDbmmMtgBasisConstSpreadModel@@QAE@ABV0@@Z

??0cDbmmMtgBasisConstSpreadModel@@QAE@PBD@Z

??0cDbmmMtgBasisModel@@QAE@ABV0@@Z

??0cDbmmMtgBasisModel@@QAE@XZ

??0cScaleFieldsSubSum@@QAE@NN@Z

??1cDbmmInterfaceCache@@QAE@XZ

??1cDbmmInterfaceControl@@QAE@XZ

??1cDbmmInterfaceEcon@@QAE@XZ

??1cDbmmInterfaceKnob@@QAE@XZ

??1cDbmmInterfaceOutput@@QAE@XZ

??1cDbmmInterfacePoolLoan@@QAE@XZ

??1cDbmmMacroEcon@@QAE@XZ

??1cDbmmMtgBasisConstSpreadModel@@UAE@XZ

??1cDbmmMtgBasisModel@@UAE@XZ

??1cScaleFieldsSubSum@@QAE@XZ

??4cDbmmInterface@@QAEAAV0@ABV0@@Z

??4cDbmmInterfaceCache@@QAEAAV0@ABV0@@Z

??4cDbmmInterfaceControl@@QAEAAV0@ABV0@@Z

??4cDbmmInterfaceEcon@@QAEAAV0@ABV0@@Z

??4cDbmmInterfaceKnob@@QAEAAV0@ABV0@@Z

??4cDbmmInterfaceOutput@@QAEAAV0@ABV0@@Z

??4cDbmmInterfacePoolLoan@@QAEAAV0@ABV0@@Z

??4cDbmmMacroEcon@@QAEAAV0@ABV0@@Z

??4cDbmmMtgBasisConstSpreadModel@@QAEAAV0@ABV0@@Z

??4cDbmmMtgBasisModel@@QAEAAV0@ABV0@@Z

??4cScaleFieldsSubSum@@QAEAAV0@ABV0@@Z

??_7cDbmmMtgBasisConstSpreadModel@@6B@

??_7cDbmmMtgBasisModel@@6B@

??_FcDbmmInterfaceOutput@@QAEXXZ

??_FcDbmmInterfacePoolLoan@@QAEXXZ

??_FcScaleFieldsSubSum@@QAEXXZ

?Add@cScaleFieldsSubSum@@QAEXNN@Z

?InitSubsum@cScaleFieldsSubSum@@QAEXNN@Z

?ReInit@cDbmmMacroEcon@@QAEX_N@Z


不知道如何通过 Java 调用这些函数。

如果有人可以为我提供 Java 端的解决方案,请提供示例代码:)

【问题讨论】:

通过 Dependency Walker 验证时的方法名称是 _Java_sysInfo_getCPUSpeed@8 如何从 Java 端解决这个问题,即类 JnaTest 需要做哪些更改才能将方法名称映射为通过依赖walker显示。 【参考方案1】:

您的函数使用 JNI 和 stdcall 约定进行修饰;它不是 C++ 损坏的。

看起来这个库是一个 JNI 库,给定了 Java_sysInfo_ 前缀。如果是这种情况,您只需要声明等效的 Java 端,例如

// default package
public class sysInfo 
    static  System.loadLibrary("Ex"); 
    public static native int getCPUSpeed();

我想你可能会发现这个映射是正确的并且你不需要 JNA。

编辑

给定一个具有任意 ctor 输入参数和方法 getCount() 的 C++ 类:

extern "C" int getCountForName(const char* name) 
    MyCPPClass mycpp(name);
    return mycpp.getCount();

Compile that into a shared library,并通过 JNA 加载。

【讨论】:

您好 Technomage,感谢您的回复。上面提到的是我正在尝试寻找解决方案的示例。用我通过 Dependency walker 浏览 dll 时找到的实际方法更新了问题。 那些 C++-mangled名称。如果它们是静态方法,您可以直接调用它们,但如果它们是成员方法,您需要编写一个暴露简单(外部“C”)函数的包装器,或者使用像JNAerator 这样的工具来生成更复杂的映射。 感谢Technomage的回复,您能否提供一个示例代码或任何参考站点,说明如何编写公开简单(外部“C”)函数的包装器?因为我是 C++ java 集成的新手。同时我正在研究 JNAerator。【参考方案2】:

如果您通过 Visual Studio 构建 DLL,如果您还没有这样做,您应该能够将其构建为发布版本而不是调试来修复函数名称。

【讨论】:

以上是关于使用 JNA 从 Java 调用 C++ dll 方法并避免方法名称修改的主要内容,如果未能解决你的问题,请参考以下文章

java使用JNA框架调用dll动态库

JNA调用DLL简单使用

Java(JNA)传递dll的引用

JNA的用法

找不到使用 JNA 调用的 DLL 所需的文件

windows下使用JNA在java中调用golang生成的dll