感谢大家支持,这几天我在IBM中文及SUN官方网站中找了不少关于JNI的文章,受益非浅,希望
有相关问题的朋友,可到那找查相关资料.
1)在 Windows 中实现 Java 本地方法
http://www-900.ibm.com/developerWorks/cn/java/jnimthds/index.shtml
2)Java Native Interface: Programmer's Guide and Specification
http://java.sun.com/docs/books/jni
上面的文章对JNI介绍非常详细,对JNI有性趣的朋友可以看看.
对于我提出的问题,我己有了成功例子,贴出来与大家分享.
有一myDll.dll库,接口Trim(LPSTR lpString),功能是把lpString中的空格去掉。文件原码
如下:
#include <string.h>
#include <windows.h>
#ifdef _cplusplus
extern "C"{
#endif
__declspec(dllexport) void Trim(LPSTR lpString)
{
int iLength = strlen(lpString);
int iCount = 0;
char *lpChar = lpString;
int i;
for(i=0;i<iLength;i++)
{
if( *lpChar != ' ' )
{
lpString[ iCount ] = *lpChar;
iCount ++;
}
lpChar ++;
}
lpString[iCount ++] = 0;
}
#ifdef _cplusplus
}
#endif
(用VC6的cl编译该文件生成动态链接库,cl -LD myDll.c -FemyDll.dll )
这里假设我们不能得到该动态链接库的原代码,只有DLL及LIB文件,在通过JNI用java调用时
,由于没有DLL原代码修改权限,需要我们自己用C/C++再做一个中介库DLL2,java调用这个
中介库,该中介库再调用原如的DLL库,间接实现java本地方法的调用。
下面是我做的例子,环境:Win2000Server,JDK1.3,VC6.0
先编一个java程序,java2dll.java,代码如下:
public class Java2dll
{
//声明要用到的本地方法cTrim由中介库(java2dll.dll)实现,它又调用了myDll.dll库的T
rim(...)函数
private native String cTrim(String str);
static
{
System.loadLibrary( "java2dll" ); //加载本地方法(java2dll为动态
链接库文件名)
}
//自己定义的一个java方法,调用本地方法cTrim(),有点多余,实际上你可直接访
问本地方法cTrim()
public String javaTrim(String str)
{
return cTrim(str);
}
//演示结果
public static void main(String[] args)
{
Java2dll ss = new Java2dll();
String str1="I love You.";
String str2=ss.javaTrim(str1);
System.out.println("调用c DLL前:");
System.out.println("str1="+str1);
System.out.println("调用c DLL后:");
System.out.println("str2="+str2);
}
}
编译该文件,生成Java2dll.class文件:
javac Java2dll.java
生成C的头文件Java2dll.h:
javah -jni Java2dll
用C编写中介库java2dll.c
#include <stdio.h>
#include "java2dll.h"
JNIEXPORT jstring JNICALL Java_Java2dll_cTrim
(JNIEnv *env, jobject obj, jstring str)
{
const char *strTemp;
strTemp=(*env)->GetStringUTFChars(env,str,NULL);
//错误出现时
if(strTemp==NULL)
{
return NULL;
}
//调用myDll.dll库中的Trim()方法,去掉strTemp中的空格
Trim(strTemp);
return (*env)->NewStringUTF(env, strTemp);
(*env)->ReleaseStringUTFChars(env,str,strTemp);
}
说明:如果你用C++编写,cpp文件,上面代码中的“(*env)”应改为“env”
编译生成java2ddl.dll文件。
用VC6的cl编译。
cl -Id:/jdk13/include -Id:/jdk13/include/win32 -LD java2dll.c myDll.lib -Fejava2dll.dll
说明:-Id:/jdk13/include指明JNI需要的头文件.h位置,D:/JDK13为我的JDK安装目录
-LD 表明要生成动态链接库
myDll.lib或 -link
myDll.lib表明要把myDll.dll链接上,否则中介库java2dll找不到Trim(..)方法。
您可用cl /?得到HELP帮助。
编译成功后,可得到中介库java2dll,运行一下,检查结果。
java Java2dll
好了,你自己试一下吧。
从 Java 程序调用 C 或 C ++ 代码的过程由六个步骤组成。我们将在下面几页中深入讨论每个步骤,但还是先让我们迅速地浏览一下它们。
编写 Java 代码。我们将从编写 Java 类开始,这些类执行三个任务:声明将要调用的本机方法;装入包含本机代码的共享库;然后调用该本机方法。
编译 Java 代码。在使用 Java 类之前,必须成功地将它们编译成字节码。
创建 C/C++ 头文件。C/C++ 头文件将声明想要调用的本机函数说明。然后,这个头文件与 C/C++ 函数实现(请参阅步骤 4)一起来创建共享库(请参阅步骤 5)。
编写 C/C++ 代码。这一步实现 C 或 C++ 源代码文件中的函数。C/C++ 源文件必须包含步骤 3 中创建的头文件。
创建共享库文件。从步骤 4 中创建的 C 源代码文件来创建共享库文件。
运行 Java 程序。运行该代码,并查看它是否有用。我们还将讨论一些用于解决常见错误的技巧。
步骤 1:编写 Java 代码 第 3 页(共16 页)
我们从编写 Java 源代码文件开始,它将声明本机方法(或方法),装入包含本机代码的共享库,然后实际调用本机方法。
这里是名为 Sample1.java 的 Java 源代码文件的示例:
1. public class Sample1
2. {
3. public native int intMethod(int n);
4. public native boolean booleanMethod(boolean bool);
5. public native String stringMethod(String text);
6. public native int intArrayMethod(int[] intArray);
7.
8. public static void main(String[] args)
9. {
10. System.loadLibrary("Sample1");
11. Sample1 sample = new Sample1();
12. int square = sample.intMethod(5);
13. boolean bool = sample.booleanMethod(true);
14. String text = sample.stringMethod("JAVA");
15. int sum = sample.intArrayMethod(
16. new int[]{1,1,2,3,5,8,13} );
17.
18. System.out.println("intMethod: " + square);
19. System.out.println("booleanMethod: " + bool);
20. System.out.println("stringMethod: " + text);
21. System.out.println("intArrayMethod: " + sum);
22. }
23. }
第二步接下来,我们需要将 Java 代码编译成字节码。完成这一步的方法之一是使用随 SDK 一起提供的 Java 编译器 javac。用来将 Java 代码编译成字节码的命令是:
javac Sample1.java
第三步是创建 C/C++ 头文件,它定义本机函数说明。完成这一步的方法之一是使用 javah.exe,它是随 SDK 一起提供的本机方法 C 存根生成器工具。这个工具被设计成用来创建头文件,该头文件为在 Java 源代码文件中所找到的每个 native 方法定义 C 风格的函数。这里使用的命令是:
javah Sample1
第四步当谈到编写 C/C++ 函数实现时,有一点需要牢记:说明必须和 Sample1.h 的函数声明完全一样。我们将研究用于 C 实现和 C++ 实现的完整代码,然后讨论两者之间的差异
以下是 Sample1.c,它是用 C 编写的实现:
1. #include "Sample1.h"
2. #include <string.h>
3.
4. JNIEXPORT jint JNICALL Java_Sample1_intMethod
5. (JNIEnv *env, jobject obj, jint num) {
6. return num * num;
7. }
8.
9. JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod
10. (JNIEnv *env, jobject obj, jboolean boolean) {
11. return !boolean;
12. }
13.
14. JNIEXPORT jstring JNICALL Java_Sample1_stringMethod
15. (JNIEnv *env, jobject obj, jstring string) {
16. const char *str = (*env)->GetStringUTFChars(env, string, 0);
17. char cap[128];
18. strcpy(cap, str);
19. (*env)->ReleaseStringUTFChars(env, string, str);
20. return (*env)->NewStringUTF(env, strupr(cap));
21. }
22.
23. JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod
24. (JNIEnv *env, jobject obj, jintArray array) {
25. int i, sum = 0;
26. jsize len = (*env)->GetArrayLength(env, array);
27. jint *body = (*env)->GetIntArrayElements(env, array, 0);
28. for (i=0; i<len; i++)
29. { sum += body[i];
30. }
31. (*env)->ReleaseIntArrayElements(env, array, body, 0);
32. return sum;
33. }
34.
35. void main(){}
步骤 5:创建共享库文件 第 13 页(共16 页)
接下来,我们创建包含本机代码的共享库文件。大多数 C 和 C++ 编译器除了可以创建机器代码可执行文件以外,也可以创建共享库文件。用来创建共享库文件的命令取决于您使用的编译器。下面是在 Windows 和 Solaris 系统上执行的命令。
Windows: cl -Ic:/jdk/include -Ic:/jdk/include/win32 -LD Sample1.c -FeSample1.dll
Solaris: cc -G -I/usr/local/jdk/include -I/user/local/jdk/include/solaris Sample1.c -o Sample1.so
最后一步是运行 Java 程序,并确保代码正确工作。因为必须在 Java 虚拟机中执行所有 Java 代码,所以需要使用 Java 运行时环境。完成这一步的方法之一是使用 java,它是随 SDK 一起提供的 Java 解释器。所使用的命令是:
java Sample1
当运行 Sample1.class 程序时,应该获得下列结果:
PROMPT>java Sample1
intMethod: 25
booleanMethod: false
stringMethod: JAVA
intArrayMethod: 33
PROMPT>
来自ibm的jni教程
这是Sun的教程
http://java.sun.com/docs/books/tutorial/native1.1/index.html
Java调用C,C调用Java