用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\win324. 编译出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)
__________________________ 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: xcl_168@aliyun.com
BLOG: http://blog.csdn.net/xcl168