Java调用C#的dll是通过C++作为桥梁,JNI—>C++的dll(clr方式运行)—>C#的dll
以下为本次测试的配置环境:
系统:win7 64位
Java开发环境(均为64位):JDK1.8、eclipse
C++开发环境:VS2015
整个工程的连接:包含JAVA、C++、C#工程,C++工程包含有生成的32位和64位的dll,C#dll通用
下面为测试详细步骤:
1、 在Java类里定义需要从C#调用的方法,方法均定义为publicnative类型,形式如下
package hui;
public class TestJNI {
public native void Init();
public native void isInLibrary();
public native boolean addData(String strDataBar,int nNum);
public native int getNum();
public native String getDataBar();
static {
System.loadLibrary("ManageDll");
}
}
2、 通过cmd命令行进入到TestJNI.java所在目录,利用javac生成TestJNI.class文件,然后利用javah生成.h的头文件,并将该头文件包含进C++工程中
执行javah命令为:javah – classpath 包所在的路径 -d . –jni hui.TestJNI
参数说明:
-classpath:class文件所在包的路径,不包含包这一层,eg:E:eclipse\TestJNI\src
-d:保存生成的.h头文件的路径
-jni:包名+类名
生成的头文件名字为:包名+类名.h;eg:hui_TestJNI.h
hui_TestJNI.h头文件内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class hui_TestJNI */
#ifndef _Included_hui_TestJNI
#define _Included_hui_TestJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: hui_TestJNI
* Method: Init
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_hui_TestJNI_Init
(JNIEnv *, jobject);
/*
* Class: hui_TestJNI
* Method: isInLibrary
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_hui_TestJNI_isInLibrary
(JNIEnv *, jobject);
/*
* Class: hui_TestJNI
* Method: addData
* Signature: (Ljava/lang/String;I)Z
*/
JNIEXPORT jboolean JNICALL Java_hui_TestJNI_addData
(JNIEnv *, jobject, jstring, jint);
/*
* Class: hui_TestJNI
* Method: getNum
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_hui_TestJNI_getNum
(JNIEnv *, jobject);
/*
* Class: hui_TestJNI
* Method: getDataBar
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_hui_TestJNI_getDataBar
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
3、 利用VS创建win32空项目工程,选择生成dll的形式
属性设置:项目——属性——常规——公共语言运行时支持——选择(公共语言运行时支持(/clr))
4、 在cpp文件中调用C#的内容需要先包含以下内容
//调用C#dll和C#的命名空间
#using “MyClassLibrary.dll”
Using namespace MyclassLibrary;
//C#在Cpp文件中的全局变量
Public ref class Globals abstract sealed {
public:
static MyClassLibrary::StrOperator^ myLibrary;
};
5、 将C# dl拷贝到C++工程源文件所在目录下,在C++工程中需要包含头文件jni.h和jni_md.h两个,可以在本机安装JDK的目录下找到,并且包含在C++工程的头文件中,如提示找不到jni.h,则在include的时候写成#include “jni.h”;还需要包含步骤2生成的头文件(hui_TestJNI.h),然后利用C#dll提供的内容定义头文件相关的函数功能;
C++文件的内容如下所示:
// CallMyLibrary.cpp : 定义控制台应用程序的入口点。
#include "jni.h"
#include "hui_TestJNI.h"
#include
#include
#include
using namespace std;
using namespace System;
using namespace msclr::interop;
//引用C#的库和命名空间
#using "MyClassLibrary.dll"
using namespace MyClassLibrary;
//定义一个和C#dll相关的全局变量
public ref class Globals abstract sealed {
public:
static MyClassLibrary::StrOperator^ myLibrary;
};
//char字符串转为jstring类型
jstring charTojstring(JNIEnv* env, const char* str)
{
jstring rtn = 0;
int slen = strlen(str);
unsigned short* buffer = 0;
if (slen == 0)
{
rtn = (env)->NewStringUTF(str);
}
else {
int length = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str, slen, NULL, 0);
buffer = (unsigned short*)malloc(length * 2 + 1);
if (MultiByteToWideChar(CP_ACP, 0, (LPCSTR)str, slen, (LPWSTR)buffer, length) > 0)
rtn = (env)->NewString((jchar*)buffer, length);
free(buffer);
}
return rtn;
}
//jstring类型转为String类型
std::string jstringtostring(JNIEnv* env, jstring jstr)
{
char* rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("UTF-8");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0)
{
rtn = (char*)malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
env->ReleaseByteArrayElements(barr, ba, 0);
std::string stemp(rtn);
free(rtn);
return stemp;
}
JNIEXPORT void JNICALL Java_hui_TestJNI_Init(JNIEnv *, jobject)
{
Globals::myLibrary = gcnew StrOperator();
Globals::myLibrary->isInLibrary();
}
JNIEXPORT void JNICALL Java_hui_TestJNI_isInLibrary(JNIEnv *, jobject)
{
cout << "in" << endl;
Globals::myLibrary->isInLibrary();
}
JNIEXPORT jboolean JNICALL Java_hui_TestJNI_addData(JNIEnv *env, jobject, jstring strDataBar, jint nNum)
{
marshal_context^ context = gcnew marshal_context();
jboolean strReturn = Globals::myLibrary->addData(context->marshal_as(jstringtostring(env, strDataBar)), nNum);
delete context;
return strReturn;
}
JNIEXPORT jint JNICALL Java_hui_TestJNI_getNum(JNIEnv *, jobject)
{
return Globals::myLibrary->getNum();
}
JNIEXPORT jstring JNICALL Java_hui_TestJNI_getDataBar(JNIEnv *env, jobject)
{
jstring rtnJstring = 0;
std::string stdstr = marshal_as(Globals::myLibrary->getDataBar()->ToString());
rtnJstring = charTojstring(env, stdstr.c_str());
return rtnJstring;
}
6、 注意JNI里面的类型对应关系,用到的String相关的转换函数,具体的可见上述代码;
7、 根据JDK的位数编译生成相应位数的dll(下面的工程连接包含32和64位的C++编译的dll,C#的dll适用于所有位数的平台),将生成的C++dll和C#dll一起拷到JDK的bin目录下,Java工程运行成功编译
本次测试遇到的问题:
运行时一直报下面的错:
#
# A fatal error has been detected by theJava Runtime Environment:
#
# Internal Error (0xe0434352), pid=7116, tid=0x00001d78
#
# JRE version: Java(TM) SE RuntimeEnvironment (8.0_121-b13) (build 1.8.0_121-b13)
# Java VM: Java HotSpot(TM) Client VM(25.121-b13 mixed mode windows-x86 )
# Problematic frame:
# C [KERNELBASE.dll+0xc54f]
#
# Failed to write core dump. Minidumps arenot enabled by default on client versions of Windows
#
# An error report file with moreinformation is saved as:
# E:\eclipse\TestJNI\hs_err_pid7116.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 VirtualMachine in native code.
# See problematic frame for where to reportthe bug.
#
应该是电脑的KERNELBASE.dll损坏,换一台电脑编译JAVA工程就成功了