自己实现一个native方法

环境

  1. Windows专业版

  2. java

    1
    2
    3
    4
    D:\桌面
    λ java -version
    java version "1.8.0_191"
    Java(TM) SE Runtime Environment (build 1.8.0_191-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)
  3. gcc——mingw-w64

    1
    2
    3
    4
    5
    6
    7
    8
    9
    D:\桌面
    λ gcc -v
    Using built-in specs.
    COLLECT_GCC=gcc
    COLLECT_LTO_WRAPPER=D:/mingw-w64/mingw64/bin/../libexec/gcc/x86_64-w64-mingw32/8.1.0/lto-wrapper.exe
    Target: x86_64-w64-mingw32
    Configured with: ../../../src/gcc-8.1.0/configure --host=x86_64-w64-mingw32 --build=x86_64-w64-mingw32 --target=x86_64-w64-mingw32 --prefix=/mingw64 --with-sysroot=/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64 --enable-shared --enable-static --disable-multilib --enable-languages=c,c++,fortran,lto --enable-libstdcxx-time=yes --enable-threads=posix --enable-libgomp --enable-libatomic --enable-lto --enable-graphite --enable-checking=release --enable-fully-dynamic-string --enable-version-specific-runtime-libs --disable-libstdcxx-pch --disable-libstdcxx-debug --enable-bootstrap --disable-rpath --disable-win32-registry --disable-nls --disable-werror --disable-symvers --with-gnu-as --with-gnu-ld --with-arch=nocona --with-tune=core2 --with-libiconv --with-system-zlib --with-gmp=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-mpfr=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-mpc=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-isl=/c/mingw810/prerequisites/x86_64-w64-mingw32-static --with-pkgversion='x86_64-posix-seh-rev0, Built by MinGW-W64 project' --with-bugurl=https://sourceforge.net/projects/mingw-w64 CFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64/opt/include -I/c/mingw810/prerequisites/x86_64-zlib-static/include -I/c/mingw810/prerequisites/x86_64-w64-mingw32-static/include' CXXFLAGS='-O2 -pipe -fno-ident -I/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64/opt/include -I/c/mingw810/prerequisites/x86_64-zlib-static/include -I/c/mingw810/prerequisites/x86_64-w64-mingw32-static/include' CPPFLAGS=' -I/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64/opt/include -I/c/mingw810/prerequisites/x86_64-zlib-static/include -I/c/mingw810/prerequisites/x86_64-w64-mingw32-static/include' LDFLAGS='-pipe -fno-ident -L/c/mingw810/x86_64-810-posix-seh-rt_v6-rev0/mingw64/opt/lib -L/c/mingw810/prerequisites/x86_64-zlib-static/lib -L/c/mingw810/prerequisites/x86_64-w64-mingw32-static/lib '
    Thread model: posix
    gcc version 8.1.0 (x86_64-posix-seh-rev0, Built by MinGW-W64 project)
  4. cmder 191012

    默认是GBK编译,切换utf-8,可用chcp 65001,切换回来用chcp 936

    在编译运行java,可以尝试-encoding,不会写的话,就百度或者去翻自己的ide。

    在编译dll是,可用-fexec-charset=gbk -finput-charset=utf-8

    我不在dll里打印中文信息,就统一GBK了,如果要换成uft8,我会崩溃的。

一、编写TestNative.java并编译

1
2
3
4
5
6
7
8
9
10
11
public class TestNative{
static{
//要写绝对路径
//System.load("TestNative.dll");
System.loadLibrary("TestNative");
}
public native static String getYouHeart(String msg);
public static void main(String[] args){
System.out.println(getYouHeart(args[0]));
}
}

  编译

1
2
3
4
5
6
7
D:\桌面                                                 
λ javac TestNative.java

D:\桌面
λ dir | find "TestNative"
2021/01/25 20:05 451 TestNative.class
2021/01/25 20:03 201 TestNative.java

二、使用javah生成.h文件

1
2
3
4
5
6
7
8
D:\桌面                                                 
λ javah -jni TestNative

D:\桌面
λ dir | find "TestNative"
2021/01/25 20:05 451 TestNative.class
2021/01/25 20:11 436 TestNative.h
2021/01/25 20:03 201 TestNative.java

  下面是生成的TestNative.h,不要随意修改。如果java里的方法没变,基本上生成一次就够了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 /* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class TestNative */

#ifndef _Included_TestNative
#define _Included_TestNative
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: TestNative
* Method: getYouHeart
* Signature: (Ljava/lang/String;)Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_TestNative_getYouHeart
(JNIEnv *, jclass, jstring);

#ifdef __cplusplus
}
#endif
#endif

三、编写.c文件

  由于我使用指定的路径总是出问题,于是就在编译时指定了。

  实现相应的方法就可。

  我的文件名TestNative.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <string.h>
//总是出奇怪的问题
//#include <D:/JAVA/8u191/JDK/include/jni.h>
//#include <D:/JAVA/8u191/JDK/include/win32/jni_md.h>
#include "jni.h"
#include "jni_md.h"
#include "TestNative.h"

JNIEXPORT jstring JNICALL Java_TestNative_getYouHeart
(JNIEnv* env, jclass obj, jstring msg){
const char* msg_char = (*env)->GetStringUTFChars(env, msg, 0);
/*在gbk下乱码,在utf8其他地方乱码*/
//printf("你说:%s\n", msg_char);
if(0 == strcmp(msg_char , "我爱你")){
//printf("%s\n", msg_char);
return (*env)->NewStringUTF(env, "我知道你的心意,我也爱你");

}else{
return (*env)->NewStringUTF(env, "你可知道我的心意?");
}
//老有问题
//(*env)->ReleaseStringUTFChars(msg,msg_char);
}

四、编译TestNative.c生成TestNative.dll

  -I后面是头文件是搜索路径,为${JAVA_HOME}下的includeinclude/win32

  -o是指定输出文件名

1
2
D:\桌面
λ gcc -o TestNative.dll -shared TestNative.c -I "D:/JAVA/8u191/JDK/include/" -I "D:/JAVA/8u191/JDK/include/win32/"

五、运行.class文件

  先前最简单的方式,在.c里打印一下。 此时乱码的原因是因为cmder的编码是GBK。用chcp 65001即可。

1
2
3
4
5
6
D:\桌面
λ javac TestNative.java

D:\桌面
λ java TestNative 我爱你
p↑Fk

  最后运行效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
D:\桌面
λ javac TestNative.java

D:\桌面
λ javah -jni TestNative
//记得编写.c文件
D:\桌面
λ gcc -o TestNative.dll -shared TestNative.c -I "D:/ JAVA/8u191/JDK/include/" -I "D:/JAVA/8u191/JDK/include/win32/"

D:\桌面
λ dir | find "TestNative"
2021/01/26 11:31 813 TestNative.c
2021/01/26 11:32 571 TestNative.class
2021/01/26 11:32 48,029 TestNative.dll
2021/01/25 21:54 456 TestNative.h
2021/01/26 10:16 284 TestNative.java

D:\桌面
λ java TestNative 你爱我吗?
你可知道我的心意?

D:\桌面
λ java TestNative 我爱你
我知道你的心意,我也爱你

我想说

  很就以前就知道有jni这个东西,当时好像是为了完成一个什么东西。这个东西用java写的话,我不会。如果用c写的话,我还可以挣扎一下。至于为什么放弃了,是因为当时看到jni的写法,感觉不会写。毕竟当时c学得不多和java理解得不多,虽然现在c也忘得差不多了。

  对于dll——动态链接库,我是在一个pvz的外挂视频里了解到的。我当时应该是很想用c来写外挂。比较没读大学前就通过套路来破解安卓游戏。我现在还记得一些关键的东西,比如onCreate6000。现在想要破解这些东西,要按照套路来已经很困难了。

参考

  1. jstring
  2. 怎样自己实现一个native本地方法呢?
  3. 自己实现一个Native方法的调用
  4. [Java] 用C编写你自己的native方法
  5. gcc -I -L -l区别
  6. linux gcc头文件搜索路径
  7. JNI基础之JNIEnv,jclass和jobject
  8. 02.JNIEnv和jclass,数据类型,访问java属性方法
  9. JNI中string 、 char* 和 jstring 两种转换
  10. char* 和jstring转换
  11. 如何将jstring转换为char *?
  12. char类型与jstring类型的转换
  13. Java JNI 创建字符串对象
  14. JNI中如何把char * 转化为jstring
  15. JNI char*转jstring乱码问题
  16. JNI官方中文资料
  17. java编译UTF-8文件乱码的问题
  18. GCC编译环境中文乱码解决方案