用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("__________________________"); } }
因為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. 2014/03/24 17:04 .. 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 . 2014/03/24 23:16 .. 2014/03/23 23:20 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>
#include "com_xcl_jini_XclJini.h" #include#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, "", "([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.(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: [email protected]
BLOG: http://blog.csdn.net/xcl168