用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: xcl_168@aliyun.com
BLOG: http://blog.csdn.net/xcl168