本文中使用的.dll和.so文件以及Java调用的代码资源:
https://download.csdn.net/download/werewolf2017/11014740
1. 编译一个dll文件或so文件
1.1 C++代码
熟悉C/C++的,可以忽略该步骤。对C/C++不熟悉,编译dll和so文件只是按部就班。
.dll文件是Windows系统的动态链接库文件,.so文件是Linux操作系统的动态链接库文件。
若想直接使用本文中的.dll和.so文件以及Java调用的代码,可以直接从https://download.csdn.net/download/werewolf2017/11014740 中下载。
首先在ide中新建dll项目,此处使用的IDE是Dev-C++。如下图所示。
main.cpp代码如下:
#include "main.h"
int add(int a,int b){
return a + b;
}
int factorial(int n){
int i;
int r = 1;
for(i=1;i<n+1;i++)
r = r*i;
return r;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
// attach to process
// return FALSE to fail DLL load
break;
case DLL_PROCESS_DETACH:
// detach from process
break;
case DLL_THREAD_ATTACH:
// attach to thread
break;
case DLL_THREAD_DETACH:
// detach from thread
break;
}
return TRUE; // succesful
}
main.h的代码如下:
#ifndef __MAIN_H__
#define __MAIN_H__
#include <windows.h>
/* To use this exported function of dll, include this header
* in your project.
*/
#ifdef BUILD_DLL
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
#ifdef __cplusplus
extern "C"
{
#endif
int add(int a,int b);
int factorial(int n);
#ifdef __cplusplus
}
#endif
#endif // __MAIN_H__
1.2 生成.dll文件
创建好main.cpp和main.h文件后,编译便得到和项目名相同的dll文件。此处项目名为DLLMain,所以编译后得到DLLMain.dll文件。
1.3 生成.so文件
要生成so文件,可以在Linux系统下,执行gcc命令编译得到。命令如下:
gcc main.h main.cpp -fPIC -shared -o hello.so
执行命令后,在main.h和main.cpp的同一级目录下,会生成hello.so文件。
Tips :部分博客中提到,使用JNA调用.so文件时,编译出来的so文件名必须lib开头。其实这个是没有这个要求的,具体可往下看。
2. 调用.dll文件和.so文件
2.1 .dll和.so文件的放置路径问题
Windows系统下,被调用的.dll文件放在classpath下即可被加载到。在maven项目中,放在resource目录下即可。如下图所示。
Linux操作系统下,.so文件直接放在classpath下是不能加载成功的。
默认情况下,JNA会从/usr/lib目录下加载.so文件。可通过添加 -Djna.library.path=xxx 来指定JNA加载.so文件的目录路径,其中xxx表示.so文件所在的目录的路径。例如,hello.so所在的目录为/root/sotest/lib,则可指定:
-Djna.library.path=/root/sotest/lib
2.2 引入JNA依赖包
此处使用maven:
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>4.2.2</version>
</dependency>
2.3 调用的demo
import com.sun.jna.Library;
import com.sun.jna.Native;
public class TestSo {
public interface ITestLib extends Library {
// Windows下,libName是.dll文件名
String libName = "DLLMain";
// Linux下,libName是.so的含后缀的文件名。
// 若不加后缀名,则编译.so文件时,文件名必须以lib开头。具体说明可往下看。
// String libName = "hello.so";
ITestLib INSTANCE = (ITestLib) Native.loadLibrary(libName, ITestLib.class);
/**
* ========= 以下是DLLMain.dll或hello.so中的对应的接口方法 =========
*/
/**
* 加法
*
* @param a
* @param b
* @return
*/
public int add(int a, int b);
/**
* 阶乘
*
* @param n
* @return
*/
public int factorial(int n);
}
public static void main(String[] args) {
int c = ITestLib.INSTANCE.add(10, 20);
System.out.println("10+20=" + c);
int d = ITestLib.INSTANCE.factorial(10);
System.out.println("10!=" + d);
}
}
2.4 运行结果
2.5 关于libName的注意点
Native.loadLibrary(String libName, class interfaceclass)方法中,libName的值有一些注意点,在上面代码中已经有提到,此处再作一次总结。
Windows操作系统下,libName的值是.dll文件去后缀文件名,例如,本文中的DLLMain.dll对应的libName的值是DLLMain。
Linux操作系统下,libName的值是.so文件的完整文件名,例如,本文中的hello.so对应的libName是hello.so。不过,若libName写成带.so后缀的形式,那么,在编译.so文件时,文件名需要以lib开头,即libhello.so,对应的libName是hello(注意,不是libhello)。