用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 时,如果包含汉字,可加上字符转换函数,来将其转为正确的字符集,否则有可能会显示乱码。