如何通过JNI调用已有非托管C/C++ DLL方法?
无需重写DLL,直接调用旧非托管C/C++库的JNI方案
你不需要重新编写C++类或生成新DLL,JNI本身支持直接绑定现有非托管库的导出函数,核心是跳过手动编写JNI桥接代码的步骤,直接映射现有函数到Java native方法。以下是具体实现步骤:
1. 确认目标DLL的函数导出信息
首先得拿到旧DLL里要调用的函数的关键信息:
- 导出函数名(注意C++的名字修饰:若原函数是
extern "C"声明的,导出名是纯函数名;否则会有包含参数类型的修饰名) - 函数的返回值类型、参数列表
- 调用约定(
__cdecl、__stdcall等,默认C++是__cdecl,Windows API常用__stdcall)
可以用Windows自带的dumpbin工具查看导出表:
dumpbin /exports your_old.dll
2. 调整Java native方法声明
修改你已有的Java包装类,让native方法的签名严格匹配目标DLL的函数:
- 方法名要和DLL的导出名一致
- 参数类型和返回值要对应JNI类型:比如C++的
int对应JNI的int,char*对应String或byte[],自定义结构体可通过long传递内存指针
示例:假设旧DLL有一个extern "C"导出的函数:
extern "C" __declspec(dllexport) int calculate(int a, int b);
对应的Java native方法声明:
public class OldDllWrapper { static { // 后续会替换为加载桥接DLL或直接用JNA加载旧DLL System.load("C:\\path\\to\\your_old.dll"); } public native int calculate(int a, int b); }
3. 动态注册JNI函数(核心步骤)
默认JNI通过生成的头文件函数名(比如Java_OldDllWrapper_calculate)匹配方法,但我们要绑定到旧DLL的calculate函数,需用RegisterNatives API手动注册映射关系,只需写一段极简的C++代码生成小的桥接DLL(并非重写旧DLL):
#include <jni.h> #include <windows.h> // 定义旧DLL函数的指针类型 typedef int (*CalculateFunc)(int, int); JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env; if (vm->GetEnv((void**)&env, JNI_VERSION_1_8) != JNI_OK) { return JNI_ERR; } // 找到Java类 jclass cls = env->FindClass("OldDllWrapper"); if (cls == nullptr) { return JNI_ERR; } // 加载旧DLL并获取函数地址 HMODULE hDll = LoadLibraryA("C:\\path\\to\\your_old.dll"); if (hDll == nullptr) { return JNI_ERR; } CalculateFunc calculate = (CalculateFunc)GetProcAddress(hDll, "calculate"); if (calculate == nullptr) { FreeLibrary(hDll); return JNI_ERR; } // 注册native方法映射 JNINativeMethod methods[] = { {"calculate", "(II)I", (void*)calculate} }; env->RegisterNatives(cls, methods, sizeof(methods)/sizeof(methods[0])); return JNI_VERSION_1_8; }
将这段代码编译成小DLL(比如jni_bridge.dll),修改Java类的静态块加载这个桥接DLL即可:
static { System.load("C:\\path\\to\\jni_bridge.dll"); }
替代方案:用JNA跳过JNI代码
如果连桥接DLL都不想写,可使用JNA(Java Native Access),它直接通过Java代码映射非托管DLL的函数,完全不需要写C++代码,用法和C#的P/Invoke类似:
import com.sun.jna.Library; import com.sun.jna.Native; public interface OldDll extends Library { OldDll INSTANCE = Native.load("your_old.dll", OldDll.class); int calculate(int a, int b); } // 调用方式 public class Main { public static void main(String[] args) { int result = OldDll.INSTANCE.calculate(10, 20); System.out.println(result); } }
内容的提问来源于stack exchange,提问作者defa0009
相关产品推荐
相关产品推荐

