JAVA通过JNI调用C#dll方法说明(包含示例)

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工程就成功了

你可能感兴趣的:(JAVA)