Java之JNI参数传递
转载:http://blog.sina.com.cn/s/blog_75a8cfac010152t3.html
一 Java端
----------------------------------------------------------------------------------begin
import java.io.UnsupportedEncodingException;
public class DESInvokeLib {
static {
System.loadLibrary("encrypt");
}
public native static byte[] encrypt(int len, byte[]str);
public native static byte[] decrypt(byte[]str);
public static void main(String[] args) {
DESInvokeLib desInvokeLib = newDESInvokeLib();
String plainText = "Welcome to My Jni Test! -wangfei/n" +
"1Welcome to My Jni Test! -wangfei/n" +
"2Welcome to My Jni Test! -wangfei/n" +
"Emai: vinch.wang@gmail.com/n";
System.out.println("plain text is: " + plainText);
try {
//将String转换为字符数组
byte[] plainTextBytes = plainText.getBytes("ISO-8859-1");
intlen = plainTextBytes.length;
System.out.println("plainTextBytes length is: " + len);
//先加密,再解密
byte[] encryptedTextBytes = desInvokeLib.encrypt(len,plainTextBytes);
byte[] decryptedTextBytes =desInvokeLib.decrypt(encryptedTextBytes);
//将字符数组转换为String
System.out.println("decryptedTextBytes length is: " +
decryptedTextBytes.length);
String decryptedText = new String(decryptedTextBytes,"ISO-8859-1");
System.out.println("decryptedText is: " + decryptedText);
}
catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
----------------------------------------------------------------------------------end
保存为DESInvokeLib.java文件, 先编译生成class文件, 再用"javahDESInvokeLib"命令生成包含dll函数导出声明的头文件"DESInvokeLib.h". 具体内容如下:
----------------------------------------------------------------------------------begin
#include <jni.h>
#ifndef _Included_DESInvokeLib
#define _Included_DESInvokeLib
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jbyteArray JNICALL Java_DESInvokeLib_encrypt
(JNIEnv *, jclass, jint, jbyteArray);
JNIEXPORT jbyteArray JNICALL Java_DESInvokeLib_decrypt
(JNIEnv *, jclass, jbyteArray);
#ifdef __cplusplus
}
#endif
#endif
----------------------------------------------------------------------------------end
二 VC端
1.在VC6的"Tool->Options"下,添加java的库文件路径.
如C:/JDK1.5.0/INCLUDE 和 C:/JDK1.5.0/INCLUDE/WIN32.
2. 建立"Win32 Dynamic-Link Library"类型的空工程, 导入DES.cpp, DES.h. DESInvokeLib.h文件.
工程名存为"encrypt".
3. 在encrypt.cpp文件内编写代码, 具体如下
----------------------------------------------------------------------------------begin
#include "windows.h"
#include "jni.h"
#include "Des.h" //参照原DES加密程序
#include "DESInvokeLib.h" //参照原DES加密程序
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
returnTRUE;
}
JNIEXPORT jbyteArray JNICALL Java_DESInvokeLib_encrypt
(JNIEnv *jniEnv, jclass, jint length, jbyteArray jarrByte)
{
//接收明文
jbyte* pJbyte =jniEnv->GetByteArrayElements(jarrByte,0);
char* szByte = (char*)pJbyte;
//显示调试信息
printf("/ncome-in: /n%s, %d/n",szByte,strlen(szByte));
//定义Key和Buffer
char key[] ={0,2,0,0,9,3,5,1,9,8,0,0,9,1,7};
char buf[255];
//存明文到buffer并加密
memset( buf, 0, sizeof(buf) );
strcpy( buf, szByte );
buf[length] = '/0';
//参照原DES加密程序
Des_Go(buf, buf, strlen(buf), key, sizeof(key), ENCRYPT);
//生成结果并返回
jbyteArray jarrRet =jniEnv->NewByteArray( strlen(buf) );
strcpy( szByte, buf );
jniEnv->SetByteArrayRegion(jarrRet, 0, strlen(buf), (jbyte *)szByte );
jniEnv->ReleaseByteArrayElements(jarrRet,pJbyte, 0);
return jarrRet;
}
JNIEXPORT jbyteArray JNICALL Java_DESInvokeLib_decrypt
(JNIEnv *jniEnv, jclass, jbyteArrayjarrByte)
{
//接收
jbyte* pJbyte =jniEnv->GetByteArrayElements(jarrByte, 0);
char* szByte = (char *)pJbyte;
//定义Key和Buffer
char key[] ={0,2,0,0,9,3,5,1,9,8,0,0,9,1,7};
char buf[256];
//存明文到buffer并加密
memset( buf, 0, sizeof(buf) );
strcpy( buf, szByte );
// 参照原DES加密程序
Des_Go(buf, buf, strlen(buf), key, sizeof(key), DECRYPT);
//生成结果并返回
jbyteArray jarrRet =jniEnv->NewByteArray( strlen(buf) );
strcpy( szByte, buf );
//显示调试信息
printf("/nout: /n%s, %d /n/n",szByte,strlen(szByte));
jniEnv->SetByteArrayRegion(jarrRet, 0, strlen(buf), (jbyte *)szByte );
jniEnv->ReleaseByteArrayElements(jarrRet,pJbyte, 0);
return jarrRet;
}
----------------------------------------------------------------------------------end
4.为方便起见,在VC6的"Project->Settings->Link->General"中,设置dll导出路径为java文件所在路径.
5. 按F7编译生成dll, 并在控制台中运行java程序, 命令如 "java DESInvokeLib", 可见结果.
三 小结
1. java与vc之间传递参数, 如果是整数类型(int), 一般不会有问题,但如果是传递字符串,则要考虑的问题较多.
java程序编译时使用的是unicode编码, 传输时使用的是utf-8格式,而vc6默认使用的是ansi编码.
2. 传递字符串, 在java端可以选择参数类型为 String 或者byte[], 如果选择String,则与vc6配合做参数传递时,问题多多,还会生成不少乱码. 如果选择使用byte[], 则处理起来容易一些,问题也要少一些.
3. 为了解决参数传递中产生的乱码问题, 所以特别在参数中加入字符串长度 len, 并在vc6收到字符串后,
用 " buf[length] = '/0' " 命令做处理. 实践表明, 效果还比较理想.
ps: 未解决的问题
1. 在vc6中接收java传递过来的字符串参数,会在正常字符串内容的末尾产生多余的乱码.
2. DES程序能处理的明文长度只到255. 即buf变量定义长度.
参考:
1. http://www.vckbase.com/code/viewcode.asp?id=1897
2. http://www.iplab.cs.tsukuba.ac.jp/~liuxj/jdk1.2/zh/docs/guide/jni/spec/jniTOC.doc.html
3. http://www.sagewire.org/java-programmer/JNI-DLL-VC-173069.aspx
1、java向c传递基本数据类型
对于基本数据类型,java和c是相互对应的,所以可以直接使用。它们的对应关系为;
------------------------------------------------------------------------
Java类型 本地类型 字节(bit)
-------------------------------------------------------------------------
boolean jboolean 8,unsigned
byte jbyte 8
char jchar 16,unsigned
short jshort 16
int jint 32
long jlong 64
float jfloat 32
double jdouble 64
void void n/a
------------------------------------------------------------------------
2.java向c传递对象类型
对于java传递进来的java对象模型,c要加载java类的原型,根据创建相应的c对象,获取java对象的方法的id,然后调用java对象的方法。举例说明:比如有个java类customer对象作为jni参数传递到c程序,customer有方法StringgetName()。
JNIEXPORTjobject JNICALLJava_com_oracle_estt_sc_db_impl_SCQueryODBC__1getCustomer
(JNIEnv*env, jobject, jobjectcustomer){
jmethodIDmethodId;
//获得customer对象的句柄
jclasscls_objClass=env->GetObjectClass(customer);
//获得customer对象中特定方法getName的id
methodId=env->GetMethodID(cls_objClass,"getName","()Ljava/lang/String;");
//调用customer对象的特定方法getName
jstring js_name=(jstring)env->CallObjectMethod(customer,methodId,NULL);
...
}
3.c向java返回对象类型
在c程序中首先要创建要返回的java对象,得到每个属性的id,然后给每个属性赋值,最后返回。举例说明:同样是customer对象,有name等属性值,需要在c程序中给每个属性赋值后返回。
JNIEXPORTjobject JNICALLJava_com_oracle_estt_sc_db_impl_SCQueryODBC__1getCustomer
(JNIEnv*env, jobject, jobjectcustomer){
......
//发现javaCustomer类,如果失败,程序返回
jclass clazz =env->FindClass("com/oracle/estt/sc/busi/Customer");
if(clazz == 0)
return 0;
//为新的java类对象obj分配内存
jobject obj = env->AllocObject(clazz);
//发现类中的属性,如果失败,程序返回
jfieldID fid_id = env->GetFieldID(clazz,"customerID","I");
if (fid_id == 0)
return 0;
jfieldID fid_name =env->GetFieldID(clazz,"name","Ljava/lang/String;");
if (fid_name == 0)
return 0;
......
env->SetIntField(obj,fid_id, 1
env->SetObjectField(obj,fid_name, jname);
......
returnobj;
}