JNI(1) Java与C/C++的交互

    用JINI来实现Java与C/C++的相互调用.感觉好麻烦,但形势就这样,没办法。 

环境:Win7+VS2012+Java 1.7

1.  编写一个Java文件,对于需要C/C++实现的方法,声明为native(本地方法)     

        里面有个System.loadLibrary即从java.library.path中指定的目录下面加载指定的动态链接库,无须指定目录和扩展名,直接在参数中输入库名称即可。

        也可以用System.load直接指定路径的方式来加载。效果是一样的。

package com.xcl.jini;

public class XclJini {

	    //声明为本地方法,生成为C/C++使用的.h 头文件中的函数声明。
	    public native int GetVersion();	    
	    public native int GetStatus();
	    public native String GetMsg();
	    public native int SendMsg(String msg);
		
		static {
			//jvm变量
			//System.out.println(System.getProperty("java.library.path"));
			//C:\java\jdk\bin
			System.loadLibrary("XclJiniLib");			
			//System.load("C:\\java\\jdk\\bin\\XclJiniLib.dll");
		}
		
	/**
	 * @param args
	 */
	public static void main(String[] args){
		System.out.println("__________________________"); 
		System.out.println("Java: jini 演示!");	
		
		XclJini _XclJini = new XclJini();		
		_XclJini.GetVersion();
		_XclJini.GetStatus();
		_XclJini.SendMsg("发个信息给C++.");
		String msg = _XclJini.GetMsg();
		System.out.println("java:"+msg);		
		System.out.println("__________________________"); 
	}
}

2. 首先通过javac编译成class,再通过javah生成供C/C++使用的.h头文件。

     因为XclJini.java中包含中文件,且是用utf-8格式存储的,所以编译时javac要加上 -encoding utf-8 参数,否则中文会显示成乱码。

      另javah时,要注意,其路径中src下,然后javah后接类路径才能生成正确的头文件

D:\AppWork\XExample\workspace\jni_demo1\src>javac -encoding utf-8 com/xcl/jini/XclJini.java

D:\AppWork\XExample\workspace\jni_demo1\src>javah com.xcl.jini.XclJini

D:\AppWork\XExample\workspace\jni_demo1\src>dir D:\AppWork\XExample\workspace\jni_demo1\src\com\xcl\jini\*.*
 驱动器 D 中的卷是 Data
 卷的序列号是 0EC2-012C

 D:\AppWork\XExample\workspace\jni_demo1\src\com\xcl\jini 的目录

2014/03/24  17:04    <DIR>          .
2014/03/24  17:04    <DIR>          ..
2014/03/24  23:15               804 XclJini.class
2014/03/24  23:14               683 XclJini.java
               2 个文件          1,487 字节
               2 个目录 19,575,050,240 可用字节

D:\AppWork\XExample\workspace\jni_demo1\src>dir
 驱动器 D 中的卷是 Data
 卷的序列号是 0EC2-012C

 D:\AppWork\XExample\workspace\jni_demo1\src 的目录

2014/03/24  23:16    <DIR>          .
2014/03/24  23:16    <DIR>          ..
2014/03/23  23:20    <DIR>          com
2014/03/24  23:16             1,046 com_xcl_jini_XclJini.h
               1 个文件          1,046 字节
               3 个目录 19,575,050,240 可用字节

D:\AppWork\XExample\workspace\jni_demo1\src>

3. C/C++对其头文件的实现.  

#include "com_xcl_jini_XclJini.h"

#include <string.h>
#include "ConvertJini.h"

/*
 * Class:     com_xcl_jini_XclJini
 * Method:    GetVersion
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_xcl_jini_XclJini_GetVersion
  (JNIEnv *, jobject)
{

	printf("C++: GetVersion() Version 1.1\n");
	return 0;
}

/*
 * Class:     com_xcl_jini_XclJini
 * Method:    GetStatus
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_xcl_jini_XclJini_GetStatus
  (JNIEnv *, jobject)
{
	printf("C++: GetStatus()\n");
	printf("C++: Running.....\n");
	printf("C++: GetStatus() end.\n");
	return 1;
}

/*
 * Class:     com_xcl_jini_XclJini
 * Method:    GetMsg
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_xcl_jini_XclJini_GetMsg
  (JNIEnv * env, jobject jobj)
{
	printf("C++: GetMsg()\n");
	char *ret = "C++ Message.";
	ConvertJini cj ;
	jstring jret = cj.stoJstring(env,ret);

	printf("C++: GetMsg() end.\n");
	return jret;
	
}

/*
 * Class:     com_xcl_jini_XclJini
 * Method:    SendMsg
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_xcl_jini_XclJini_SendMsg
  (JNIEnv * env, jobject jobj, jstring msg)
{
	printf("C++: SendMsg()\n");
	
	jboolean  b  = true;
    char s[80];	
    memset(s, 0, sizeof(s));
    strcpy_s(s ,(char*)env->GetStringUTFChars(msg, &b));
    printf("C++: Java Message:%s\n", s);
    env->ReleaseStringUTFChars(msg , NULL);

	printf("C++: SendMsg() end.\n");
	return 0;
}

 要注意的地方之一是,GetStringUTFChars后,要记得用ReleaseStringUTFChars来释放空间,否则会造成内存泄漏。


char*与jstring的相互转换行数,这个感觉好麻烦.

//jstring to char*
char* ConvertJini::jstringTostring(JNIEnv* env, jstring jstr)
{        
	errno_t err;
	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);

		err = memcpy_s(rtn,alen + 1, ba, alen);
		rtn[alen] = 0;
	}
	env->ReleaseByteArrayElements(barr, ba, 0);
	return rtn;
}

//char* to jstring
jstring ConvertJini::stoJstring(JNIEnv* env, const char* pat)
{
	size_t maxlen = 500;

	jclass strClass = env->FindClass("Ljava/lang/String;");
	jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
	jbyteArray bytes = env->NewByteArray(strnlen(pat,maxlen));
	env->SetByteArrayRegion(bytes, 0, strnlen(pat,maxlen), (jbyte*)pat);
	jstring encoding = env->NewStringUTF("utf-8");
	return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);
} 


def文件:

LIBRARY	"XclJiniLib"
EXPORTS
Java_com_xcl_jini_XclJini_GetVersion    @1 


C/C++需要从JDK中引入头文件jini.h,才能做编译。

  C:\java\jdk\include;C:\java\jdk\include\win32

4. 编译出dll文件,将其用load或loadLibrary来加载C++动态库。

    例子中,我将其复制到了C:\java\jdk\bin 下。

    编译时要注意是32位还是64位,如位数不对,Java加载时会报下面的错:      

Exception in thread "main" java.lang.UnsatisfiedLinkError: C:\java\jdk\bin\XclJiniLib.dll: Can't load IA 32-bit .dll on a AMD 64-bit platform
	at java.lang.ClassLoader$NativeLibrary.load(Native Method)
	at java.lang.ClassLoader.loadLibrary1(ClassLoader.java:1939)
	at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1864)
	at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1854)
	at java.lang.Runtime.loadLibrary0(Runtime.java:845)
	at java.lang.System.loadLibrary(System.java:1084)
	at com.xcl.jini.XclJini.<clinit>(XclJini.java:14)


5. 运行结果如下.   

__________________________
Java: jini 演示!
java:C++ Message.
__________________________
C++: GetVersion() Version 1.1
C++: GetStatus()
C++: Running.....
C++: GetStatus() end.
C++: SendMsg()
C++: Java Message:发个信息给C++.
C++: SendMsg() end.
C++: GetMsg()
C++: GetMsg() end.
    发现Java的都显示中前面,C/C++的printf输出的都显示中后面。

   在C/C++中接由到Java的jstring 时,如果包含汉字,可加上字符转换函数,来将其转为正确的字符集,否则有可能会显示乱码。

MAIL: [email protected]

BLOG: http://blog.csdn.net/xcl168


你可能感兴趣的:(java,javac,cc++,javah,JINI)