JNI调用C#的dll

解决问题的思路

JNI不能直接调用C#的dll,原因是C#没有导出函数,C#的本地方法不能被java加载

可以使用C++做一个桥接,通过C++将C#的dll封装,java再调用C++的dll,即

:java →C++ wrapper → C#

遇到的问题及解决方案

网上有很多这方面的教程,但是中间有很多坑,没有讲明白,会导致很多错误。

jni

public class TestJNI{
    public native int add(int a, int b);
    static {
        System.loadLibrary("wrapper");
    }
}
上述代码需要注意两个问题:

1、需要加载的dll文件为wrapper.dll,但是不能写成System.loadLibrary("wrapper.dll"),这样JNI会去查找wrapper.dll.dll。

2、wrapper.dll加载路径在哪里: jni会从java.library.path的路径里查找指定dll文件。

当然,把生成的dll放到配置好的环境变量里面是一件麻烦的事情,可以通过如下方式加载指定路径的dll文件

jni

public class TestJNI{
    public native int add(int a, int b);
    static {
               String value = System.getProperty("java.library.path");
               value = value + ";" +  new File("").getAbsolutePath();
               System.setProperty("java.library.path", value);
        System.loadLibrary("wrapper");
    }
}
 

如此,只用加需要将加载的dll放到工程根目录下即可

特别需要注意的问题

由于我们的wapper.dll只是一个封装器,是用C++写的dll,wrapper会依赖C#写的真正的实现方法add.dll,如果将add.dll和wrapper.dll放到

同一个路径下,java会抛出如下错误:

jni

#
# A fatal error has been detected by the Java Runtime Environment:
#
#  Internal Error (0xe0434f4d), pid=5252, tid=11108
#
# JRE version: Java(TM) SE Runtime Environment (7.0_80-b15) (build 1.7.0_80-b15)
# Java VM: Java HotSpot(TM) Client VM (24.80-b11 mixed mode windows-x86 )
# Problematic frame:
# C  [KERNELBASE.dll+0xc54f]
#
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# E:\myfile\project\C-plus-plus\javassqlite\hs_err_pid5252.log
#
# If you would like to submit a bug report, please visit:
#   http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.

打开hs_err_pid5252.log你会发现这么一句话:

jni

Internal exceptions (2 events):
Event: 0.020 Thread 0x0084e000 Threw 0x0460ec20 at C:\re\jdk7u80\2329\hotspot\src\share\vm\prims\jni.cpp:3991
Event: 0.617 Thread 0x0084e000 Threw 0x046b9080 at C:\re\jdk7u80\2329\hotspot\src\share\vm\prims\jvm.cpp:1319

而jni.cpp第3991行是这样的:http://hg.openjdk.java.net/jdk7u/jdk7u60/hotspot/file/tip/src/share/vm/prims/jni.cpp

jni

static bool register_native(KlassHandle k, Symbol* name, Symbol* signature, address entry, TRAPS) {
  methodOop method = Klass::cast(k())->lookup_method(name, signature);
  if (method == NULL) {
    ResourceMark rm;
    stringStream st;
    st.print("Method %s name or signature does not match",
             methodOopDesc::name_and_sig_as_C_string(Klass::cast(k()), name, signature));
    THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false); //这是第3991行
  }
  ...
}

从错误中看好像是没找到包装器中的本地方法,其实这不是真正的原因。

通过排查发现,如果包装器中的方法不引用任何C#dll的内容,是没有问题的,只有包装器中的本地方法引用了C#的方法,才会出现问题。

换成jna同样会有这样的问题。只是抛错的形式不一样。

后来通过排查发现,是java没有加载到C++的dll依赖的C#的dll。

如何才能让java加载到C++的dll依赖的C#的dll呢?

一篇文章的提问中发现了思路:https://stackoverflow.com/questions/28093463/unresolved-link-error-using-jni-to-load-c-sharp-into-java-project

其中有一个网友回答:

 

So to figure out this problem, you can usethe process monitor to monitor the java.exe to see how it loading the DLLs. AsI observed,the java exe will try find the C# DLL in:

1、First in global assembly cache.(GAC). Only strongname assemblies are in GAC.
2、The folder of java.exe.

jni虽然在自己指定的路劲中找到了wrapper.dll,但是wrapper.dll所依赖的add.dll并没有被加载到内存中去,jni在调用wrapper.dll中的add方法时,会加载C#的add.dll的add方法,

此时内存中没有该方法,因为没有加载到C#的dll,从而导致错误。

解决方法是将add.dll放到java的运行时环境所用的bin目录中去,即“Thefolder of java.exe.”,从而让java能加载到所有C++的dll内部依赖的C#的dll。

特别重要的问题

javah生成的头文件是根据java类的路径命名的,如果java类改变了,头文件需要重新生成,否则就不能找到原来的方法了!!

其余问题网上都有答案。这几个问题比较隐晦,记录一下,以便后面有用到的时候可以借鉴

参考

参考视频:https://www.youtube.com/watch?v=tI0tFUjeffA

参考文档:https://www.codeproject.com/Articles/378826/How-to-wrap-a-Csharp-library-for-use-in-Java


你可能感兴趣的:(JNI调用C#的dll)