1. 1 不可撤销
  2. 2 小年兽 程嘉敏
  3. 3 手放开 李圣杰
  4. 4 迷人的危险3(翻自 dance flow) FAFA
  5. 5 山楂树之恋 程佳佳
  6. 6 summertime cinnamons / evening cinema
  7. 7 不谓侠(Cover 萧忆情Alex) CRITTY
  8. 8 神武醉相思(翻自 优我女团) 双笙
  9. 9 空山新雨后 音阙诗听 / 锦零
  10. 10 Wonderful U (Demo Version) AGA
  11. 11 广寒宫 丸子呦
  12. 12 陪我看日出 回音哥
  13. 13 春夏秋冬的你 王宇良
  14. 14 世界が终わるまでは… WANDS
  15. 15 多想在平庸的生活拥抱你 隔壁老樊
  16. 16 千禧 徐秉龙
  17. 17 我的一个道姑朋友 双笙
  18. 18 大鱼  (Cover 周深) 双笙
  19. 19 霜雪千年(Cover 洛天依 / 乐正绫) 双笙 / 封茗囧菌
  20. 20 云烟成雨(翻自 房东的猫) 周玥
  21. 21 情深深雨濛濛 杨胖雨
  22. 22 Five Hundred Miles Justin Timberlake / Carey Mulligan / Stark Sands
  23. 23 斑马斑马 房东的猫
  24. 24 See You Again Wiz Khalifa / Charlie Puth
  25. 25 Faded Alan Walker / Iselin Solheim
  26. 26 Natural J.Fla
  27. 27 New Soul Vox Angeli
  28. 28 ハレハレヤ(朗朗晴天)(翻自 v flower) 猫瑾
  29. 29 像鱼 王贰浪
  30. 30 Bye Bye Bye Lovestoned
  31. 31 Blame You 眠 / Lopu$
  32. 32 Believer J.Fla
  33. 33 书信 戴羽彤
  34. 34 柴 鱼 の c a l l i n g【已售】 幸子小姐拜托了
  35. 35 夜空中最亮的星(翻自 逃跑计划) 戴羽彤
  36. 36 慢慢喜欢你 LIve版(翻自 莫文蔚) 戴羽彤
  37. 37 病变(翻自 cubi) 戴羽彤
  38. 38 那女孩对我说 (完整版) Uu
  39. 39 绿色 陈雪凝
  40. 40 月牙湾 LIve版(翻自 F.I.R.) 戴羽彤
夜空中最亮的星(翻自 逃跑计划) - 戴羽彤
00:00 / 04:10

夜空中最亮的星 能否听清

那仰望的人 心底的孤独和叹息

夜空中最亮的星 能否记起

那曾与我同行 消失在风里的身影

我祈祷拥有一颗透明的心灵

和会流泪的眼睛

给我再去相信的勇气

越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请指引我靠近你

夜空中最亮的星 是否知道

那曾与我同行的身影 如今在哪里

夜空中最亮的星 是否在意

是等太阳先升起 还是意外先来临

我宁愿所有痛苦都留在心底

也不愿忘记你的眼睛

哦 给我再去相信的勇气

哦 越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请照亮我向前行 哒~

我祈祷拥有一颗透明的心灵

和会流泪的眼睛 哦

给我再去相信的勇气

哦 越过谎言去拥抱你

每当我找不到存在的意义

每当我迷失在黑夜里

噢喔喔 夜空中最亮的星

请照亮我向前行

java通过JNA调用动态库

前言

老规矩,先说下为什么会有这篇文章。近期对接了一个项目,应接口提供方要求,必须通过动态库调用,一个是为了安全可控,调用方不用知道内部实现,加密、解密、具体的逻辑不需要考虑,只需要调用即可;另一个是封装了统一的GUI界面。总之就是非用动态库不可,然后我查了很多资料,请教了几个大佬,最后在运气的加持下,终于调通了,但整个过程特别坎坷,所以我觉有必要记录下。需要说明的是我们这里采用的是JNA的方式

什么是动态库

说实话,一般我们不会有调用动态库的需求,因为这不是web开发的范畴,出发你涉及到嵌入式的开发,或者客户端开发。动态库也叫动态链接库,英文简写DLL,简单来讲,就是Windows下开发的程序模块,类似于java下的jar(不知道可不可以这样 理解)。它是实现Windows应用程序共享资源、节省内存空间、提高使用效率的一个重要技术手段。windows下它是以dll结尾的文件,比如:msctf.dll

百度百科给的解释是这样的:

动态链接库英文为DLL*,是Dynamic Link Library*的缩写。DLL是一个包含可由多个程序,同时使用的代码和数据的库。在Windows中,这种文件被称为应用程序拓展。例如,在 Windows 操作系统中,Comdlg32.dll 执行与对话框有关的常见函数。因此,每个程序都可以使用该 DLL 中包含的功能来实现“打开”对话框。这有助于避免代码重用和促进内存的有效使用。 通过使用 DLL,程序可以实现模块化,由相对独立的组件组成。例如,一个计账程序可以按模块来销售。可以在运行时将各个模块加载到主程序中(如果安装了相应模块)。因为模块是彼此独立的,所以程序的加载速度更快,而且模块只在相应的功能被请求时才加载。

咱也不是特别知道,咱也不敢问,你现在只有保证知道动态库这样的东西就行了。

开整

Talk is cheap. Show me the code。先上代码,然后再解释

public class DllTest {
    static {
        String filePath = "D:\\dll\\"; // 这里是你的动态库所在文件夹的绝对路径
        // 这里引用动态库和他的依赖
        System.load(filePath + "mfc100.dll");   
        System.load(filePath + "mydll.dll");        
    }

    public static void main(String[] args) {
        String strUrl = "http://127.0.0.1/test";
        String InData = "{\"data\":{\"operatorId\":\"test001\",\"operatorName\":\"超级管理员\",\"orgId\":\"123\"},\"orgId\":\"1232\"}";
        byte[] OutData = new byte[1024];

        String msg = CLibrary.INSTANCE.test(strUrl.getBytes(), InData.getBytes(), OutData);
        System.out.println(msg);
        try {
            System.out.println(new String(OutData, "GBK"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

    }
    // 这里是最关键的地方
    public interface CLibrary extends Library {
        // FS_CheckCode是动态库名称,前面的d://test//是路径
        CLibrary INSTANCE = (CLibrary) Native.loadLibrary("mydll", CLibrary.class);

        // 我们要调用的动态库里面的方法。
        String test(byte[] strUrl, byte[] InData, byte[] OutData);
    }
}

动态库里面的方法是这么定义的:

char* __stdcall test(char* strUrl,char* InData,char* OutData)

解释

小朋友,你是否有很多的问号❓😂没事,接下来我们就详细说明下。首先要关注的是java定义动态库接口方法,对应代码:

public interface CLibrary extends Library {
        // FS_CheckCode是动态库名称,前面的d://test//是路径
        CLibrary INSTANCE = (CLibrary) Native.loadLibrary("mydll", CLibrary.class);

        // 我们要调用的动态库里面的方法。
        String test(byte[] strUrl, byte[] InData, byte[] OutData);
    }

其中,loadLibrary方法是创建动态库对象实例,第一入参是你要调用的动态库的名字,test方法对应动态库中的方法,这里需要注意的是jNA和动态库直接数据类型的对应关系,具体的对应看后面的附表。

这里还有一个需要注意的问题是是,动态库加载的问题:

// 这里引用动态库和他的依赖
        System.load(filePath + "mfc100.dll");   
        System.load(filePath + "mydll.dll"); 

如果你没有把动态库放到classpath下,而且没有上面加载的代码,会报如下错误:

Exception in thread "main" java.lang.UnsatisfiedLinkError: Unable to load library 'NationECCode':
找不到指定的模块。

遇到的问题

JDK版本

如果完成了相应改造工作,你就可以直接运行了。如果你的JDK是64位,但你的动态库是32位(X86),它肯定会报如下错误:

java.lang.UnsatisfiedLinkError: D:\workspace\learning\github\httputil-demo\dll\mydll.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.loadLibrary0(ClassLoader.java:1941)
	at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1824)
	at java.lang.Runtime.load0(Runtime.java:809)
	at java.lang.System.load(System.java:1086)

或者这样:

Exception in thread "main" java.lang.UnsatisfiedLinkError: %1 不是有效的 Win32 应用程序。

	at com.sun.jna.Native.open(Native Method)
	at com.sun.jna.NativeLibrary.loadLibrary(NativeLibrary.java:278)
	at com.sun.jna.NativeLibrary.getInstance(NativeLibrary.java:455)
	at com.sun.jna.Library$Handler.<init>(Library.java:179)
	at com.sun.jna.Native.loadLibrary(Native.java:646)
	at com.sun.jna.Native.loadLibrary(Native.java:630)

当然如果你的动态库是64位,JDK是32位(X86),同样也会报错(应该会,没试过)

解决方法很简单:

  • 更换JDK版本
  • 联系动态库封装的人,重新封装对应的版本

找不到模块

这个问题上面已经说过了,就是因为没有加载动态库文件,而且也没有把它和它的依赖文件放到classpath下,就会报这个错。

数据类型错误

这个问题本质上就是没有搞清楚JNA和动态库数据对应关系,我之前也没搞清楚,反复试了好多次才成功。然后在今天写这篇文章的时候,发现了char*作为出参和入参对应的类型是不一样的,才恍然大悟。希望小伙伴在自己搞的时候一定看清楚。

出参未分配空间

和java不一样,动态库方法是把入参传给方法的,而且需要给出参分配空间,如果不分配内存空间,会报错:

 java.lang.Error: Invalid memory access

这个问题也很好解决,就是给出参分配足够的空间:

byte[] OutData = new byte[1024];

到这里,所有问题都解决了,动态库也完美运行起来了。

总结

收获就一句话:对于自己没有做过的事,要积极尝试,积极思考,积极请教,然后问题解决后要积极分享。好了,祝大家周末愉快!

JNA和动态库类型之间的映射关系:

Native Type Size Java Type Common Windows Types
char 8-bit integer byte BYTE, TCHAR
short 16-bit integer short WORD
wchar_t 16/32-bit character char TCHAR
int 32-bit integer int DWORD
int boolean value boolean BOOL
long 32/64-bit integer NativeLong LONG
long long 64-bit integer long __int64
float 32-bit FP float
double 64-bit FP double
char* C string String LPCSTR
void* pointer Pointer LPVOID, HANDLE, LPXXX

补充表:

Native Type Java Type Native Representation
char byte 8-bit integer
wchar_t char 16/32-bit character
short short 16-bit integer
int int 32-bit integer
int boolean 32-bit integer (customizable)
long, __int64 long 64-bit integer
long long long 64-bit integer
float float 32-bit FP
double double 64-bit FP
pointer Buffer/Pointer
pointer array [] (array of primitive type)
char* String
wchar_t* WString
char** String[]
wchar_t** WString[]
void* Pointer
void ** PointerByReference
int& IntByReference
int* IntByReference
struct Structure
(*fp)() Callback
varies NativeMapped
long NativeLong
pointer PointerType

然后又找到一些补充资料:

C语言 Java
char* String (作为入口参数)/ byte[] (作为出口参数)
unsigned char* String (作为入口参数)(不确定,没具体使用过)/ Pointer (作为出口参数)
int* IntByReference
posted @ 2020-05-24 10:56  云中志  阅读(2981)  评论(0编辑  收藏  举报