jni调用三方dll实例(绝对简单可用)
一.废话在前(可不看):
自学难啊,我是个懒人,一般情况下,是非常不喜欢写东西的,更不想转载别人的资料然后骗分、骗人气。只有自己经过非常惨痛的教训然后最后百度以后,还是没有办法解决的情况下,然后最终无意中搞定了,这种情况下,我写写,尽量减少大家走到弯路,一开始我打算百度,然后照葫芦画瓢,资料我收集了一堆,调了N++久,可惜这个瓢还是没画好,最终,我还去书店买书,可惜也没有找到相关资料, 还是最终自己想通了这个过程,才搞定,废话完毕,资料如下:
二.准备工作:
先配置环境,
1. 配置java环境,最起码要保证在cmd里面可以运行java、javac、javah命令。
2. 准备一个vc++的环境吧,我用的是vc2010。
3. 先将java安装目录下的C:\Program Files\Java\jdk1.5.0_11\include下面的jni.h文件和
C:\Program Files\Java\jdk1.5.0_11\include\win32的jni_md.h文件拷贝到vc环境下的include文件下面
环境就这样了,完成了。
备注(可不看):
加点便于理解的心得,实际上,jni是用java调用自己写的dll在通过这个dll调用第三方的dll,才开始了,我将自己写的这个dll看的很神奇了,完全照网上的资料写,写的我哭,最终我搞定了,我才发现,原来,自己写的这个dll仍然是c++的dll,说起来和java是没有关系的,之所以像java,其原因是最终有个include <jni>, 实际上我最终解决的办法,就是请了一位兄弟,帮我写了一个c++调用的代码,根据他的代码,我先完成了c++调用一个dll在通过这个dll调用三方的dll,成功以后, 我修改了中间的dll,在使用java类调用,居然成功了。对了,在多说一句,c++有个调用限制的关键字不要用啊,就默认就好,不过那个名字编译的c是需要的。废话完毕,代码操作如下:
三.代码操作如下(为便于理解,我参数传递就用int好了):
1. 先新建一个dll当作三方的dll,操作如下:
A.新建项目
打开vc++环境,新建项目—win32项目(修改路径自定义,输入项目名称fmydll) —下一步—选择dll—选择空项目(便于理解)—项目名称上面点击右键—属性 —配置属性—常规—点击字符集—修改为使用多字节字符集(为防止会报错)
B.新建.h文件
在项目的头文件夹上点击右键—添加新建项—选中头文件(.h)—名称输入fmydll 拷贝代码如下:
#ifndef fmydll_H_
#define fmydll_H_
extern "C" int _declspec(dllexport) fMax(int a);
#endif
// extern "C"让编译的名称不改变,_declspec(dllexport)这个好像是必须的
// fMax(int a)这个是我申明的方法
C.新建cpp文件
在项目的源文件夹上点击右键—添加新建项—选中c++文件(.cpp)—名称输
入fmydll拷贝代码如下:
#include "fmydll.h"
#include<iostream>
extern "C" int _declspec(dllexport) fMax(int a)
{
printf("调用函数tesdll的函数func_test1:返回值为%d\n");
return a*10;
}
D.生成dll
点击生成—重新生产解决方案—关闭这个解决方案。
三方的dll生成就算完成了,然后到这个项目的目录下面debug文件夹下把fmydll.dll拷贝出来,放在一边待用。
2. 新建java调用类,操作如下(可单独建立一个文件夹比如叫jni):
新建文本文档,更改其名称为testdll.java然后拷贝如下代码:
public class testdll
{
static
{
System.loadLibrary("mydll");
}
public native static int Max(int a);
public static void main(String[] args)
{
testdll test = new testdll();
test.Max(10);
System.out.println(test.Max(1)); //可更改参数看效果
}
}
保存关闭以后,cmd界面cd进入这个目录,输入javac testdll.java回车产生.class文件,然后javah testdll产生.h文件,现在可以把刚刚那个第三方的fmydll.dll拷贝进来只用拷贝这个文件,就可以了。现在jni文件夹里有4个文件。
3. 新建自己写的中间dll文件(就是这里啊,卡了我N++天啊):
A.新建项目
打开vc++环境,新建项目—win32项目(修改路径自定义,输入项目名称mydll) —下一步—选择dll—选择空项目—项目名称上面点击右键—属性—配置属性—
常规—点击字符集—修改为使用多字节字符集
B.新建.h文件
在项目的头文件夹上点击右键—添加新建项—选中头文件(.h)—名称输入mydll
拷贝刚刚javah生产的.h中代码到这个文件中,代码如下(我看都没看,直接复制过来):
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class testdll */
#ifndef _Included_testdll
#define _Included_testdll
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: testdll
* Method: Max
* Signature: (I)I
*/
JNIEXPORT jint JNICALL Java_testdll_Max
(JNIEnv *, jclass, jint);
#ifdef __cplusplus
}
#endif
#endif
C.新建cpp文件
在项目的源文件夹上点击右键—添加新建项—选中c++文件(.cpp)—名称输入mydll拷贝代码如下:
#include <windows.h>
#include <jni.h>
#include "mydll.h"
typedef int( *abc)(int);
JNIEXPORT jint JNICALL Java_testdll_Max(JNIEnv *, jclass, jint a)
{
abc ab;
int k=0;
HINSTANCE hDLL=LoadLibrary("fmydll.dll");
ab=(abc)GetProcAddress(hDLL,"fMax");
if(ab)
{
k=ab(a);
}
return k+1;
}
D.生成dll
点击生成—重新生产解决方案—关闭这个解决方案。
将这个mydll.dll拷贝到刚刚那个jni文件夹下,现在这个文件夹下有5个文件了, 1个.java文件,1个.class文件,1个.h文件,两个.dll文件然后运行java testdll就可以看到效果了。
备注(重要啊):我的代码完成以后,在我的环境下测试没有问题,我就放在了,csdn上,结果下载下来以后,对方告诉我无法使用,是直接使用我打包好的文件,我很奇怪,详细询问其测试环境,没发现问题,自己测试以后仍然没有问题,其和我是环境唯一之差别是其是vc6.0,无奈之下,建立了一个虚拟机,安装vc6.0,测试发现果然无法使用,已经打包好的dll居然仍然受到环境影响,这个不太懂原因,反正,请大家注意了。
补充:
1.我的两个dll文件,一个是将传过来的参数直接乘10,一个是将传过来的值+1所以,比如我传值是1那结果就是1*10+1=11,依次类推。
2.至于参数传递的难题,看看再说吧。
第二篇:java调用dll
本章用Hello World示例带你领略JNI编程。
2.1 Overview
准备过程:
1. 创建一个类(HelloWorld.java)
2. 使用javac编译该类
3. 利用javah -jni产生头文件
4. 用本地代码实现头文件中定义的方法
5. Run
译注:
在一个特定环境中,写本地实现的过程是不同的(如Android)。
javah主要是生成头文件和函数签名(每个方法和成员都有签名,后有详细介绍),通过
javah学习如何正确的写法。
注意:如上述HelloWorld.java,编译后的文件为HelloWorld.class, 用 $javah HelloWorld
来产生头文件,不要带末尾的".class"。
2.2 Declare the Native Method
HelloWorld.java
class HelloWorld {
private native void print();
public static void main(String[] args) {
new HelloWorld().print();
}
static {
System.loadLibrary("HelloWorld");
}
}
HelloWrold类首先声明了一个private native print方法. static那几行是本地库。
在Java代码中声明本地方法必须有"native"标识符,native修饰的方法,在Java代码中
只作为声明存在。
在调用本地方法前,必须首先装载含有该方法的本地库. 如HelloWorld.java中所示,置
于static块中,在Java VM初始化一个类时,首先执行这部分代码,这可保证调用本地方
法前,装载了本地库。
装载库的机制,后有介绍。
2.3 Compile the HelloWorld Class
$javac HelloWorld.java
2.4 Create the Native Method Header File
$javah -jni HelloWorld
译者:"-jni"为默认参数,可有可无.
上述命令,生成HelloWorld.h文件. 关键部分如下:
JNIEXPORT void JNICALL
Java_HelloWorld_print (JNIEnv *, jobject);
现在,请先忽略两个宏:JNIEXPORT和JNICALL。 你会发现,该函数声明,接受两个参数,
而对应的Java代码对该函数的声明没有参数。第一个参数是指向JNIEnv结构的指针; 第
二个参数,为HelloWorld对象自身,即this指针。
译注:
JNIEnv是JNI核心数据之一,地位非常崇高,所有对JNI的调用都要通过此结构。
2.5 Write the Native Method Implementation
必须根据javah生成的本地函数声明实现函数,如下:
#include <jni.h>
#include <stdio.h>
#include "HelloWorld.h"
JNIEXPORT void JNICALL
Java_HelloWorld_print(JNIEnv *env, jobject obj)
{
printf("Hello World!\n");
return;
}
请注意:"jni.h"文件必须被包含,该文件定义了JNI所有的函数声明和数据类型。
2.6 Compile the C Source and Create a Native Library
请注意,生成的本地库的名字,必须与System.loadLibrary("HelloWorld");待装载库
的名字相同。
Solaris:
$cc -G -I/java/include -I/java/include/solaris HelloWorld.c -o libHelloWorld.so
-G: 生成共享库
Win:
$cl -Ic:\java\include -Ic:\java\include\win32 -MD -LD HelloWorld.c -FeHelloWorld.dll
-MD:保证与Win32多线程C库连接(译者:Win上分静态、动态、动态多线程...C库)
-LD: 生成动态链接库
2.7 Run the Program
Solaris or Win:
$java HelloWorld
输出:
Hello World!
运行前,必须保证连接器,能找到待装载的库,不然,将抛如下异常: java.lang.UnsatisfiedLinkError: no HelloWorld in library path
at java.lang.Runtime.loadLibrary(Runtime.java)
at java.lang.System.loadLibrary(System.java)
at HelloWorld.main(HelloWorld.java)