Java使用jniwrapper向DLL传递uint64_t类型回调指针的问题
解决jniwrapper传递回调函数指针为uint64_t类型的方案
你遇到的问题核心是第三方DLL要求将回调指针以uint64_t数值的形式存储在结构体中,而直接映射为Pointer/Void类型会传入jniwrapper封装的对象结构,不符合DLL的预期,按以下步骤操作即可解决:
步骤1:调整结构体的Java映射定义
结构体中的callback字段不要映射为Pointer/Void类型,要直接映射为jniwrapper提供的UInt64类型,和DLL的结构体定义完全对齐:
import com.jniwrapper.Structure; import com.jniwrapper.UInt64; public class RandomStruct extends Structure { // 按DLL定义的顺序添加所有其他结构体字段,此处省略 private UInt64 callback = new UInt64(); public RandomStruct() { // 按结构体实际字段的先后顺序依次注册所有字段 // addField(其他字段1); addField(callback); // addField(其他字段2); } // 提供设置回调指针的方法 public void setCallbackValue(long ptrValue) { this.callback.setValue(ptrValue); } }
步骤2:定义符合DLL要求的回调类
根据DLL要求的回调签名、调用约定(stdcall/cdecl)定义对应的回调类,jniwrapper中需要继承Callback类,如果是stdcall约定还需要实现StdCallLibrary.StdCallCallback接口:
import com.jniwrapper.Callback; import com.jniwrapper.Int; import com.jniwrapper.win32.StdCallLibrary; // 示例:假设DLL要求的回调签名为 void callback(int eventId),调用约定为stdcall public class CustomCallback extends Callback implements StdCallLibrary.StdCallCallback { @Override public void run() { // 按DLL定义的参数顺序依次读取入参 Int eventId = new Int(); readParameter(eventId); // 此处编写实际的回调业务逻辑 System.out.println("触发回调,事件ID:" + eventId.getValue()); } }
步骤3:获取回调原生指针并传入结构体
创建回调实例后,通过jniwrapper的API获取其原生指针的数值,再存入结构体的UInt64字段即可:
// 1. 实例化回调,必须全程持有该实例的强引用,不能被GC回收,否则指针会失效 CustomCallback myCallback = new CustomCallback(); // 2. 获取回调对应的原生指针的64位数值 long callbackPtr = myCallback.getPointer().getPeer(); // 3. 构造结构体并设置回调字段 RandomStruct struct = new RandomStruct(); struct.setCallbackValue(callbackPtr); // 4. 后续正常将struct传递给DLL的对应调用函数即可
注意事项
- 回调的调用约定必须和DLL要求完全匹配,否则会出现参数读取错误、程序崩溃等问题
- 回调实例必须持有强引用,建议存为类的成员变量而非局部变量,避免Java GC回收后DLL调用野指针
- 不需要使用回调后,手动调用
myCallback.dispose()释放原生资源,避免内存泄漏
内容的提问来源于stack exchange,提问作者Jack
相关产品推荐
相关产品推荐

