我可以用 Rust 创建一个回调包装器吗?

问题描述 投票:0回答:0

我想从 Rust 程序中调用本机 Windows

CopyFileEx
函数,但我无法获得
LPPROGRESS_ROUTINE
的工作示例。我是一名学习 Rust 的 Java 程序员,缺乏 OOP 范式对我来说是一个挑战。在 Java 中,我会使用一个实现接口的类作为我的回调。但是,
lpprogressroutine
参数看起来像是指向函数的指针,而不是多态对象。但这应该没问题;我可以只声明一个函数并创建一个指向它的指针。

我希望回调函数调用带有附加参数的不同函数,所以我创建了一个包装器结构来包含这些附加参数和回调函数:

use jni::objects::{JObject, JString, JValueGen};
use jni::strings::JavaStr;
use jni::sys::jint;
use jni::JNIEnv;
use windows::core::*;
use windows::Win32::Foundation::HANDLE;
use windows::Win32::Storage::FileSystem::{
    CopyFileExA, LPPROGRESS_ROUTINE, LPPROGRESS_ROUTINE_CALLBACK_REASON,
};

struct Callback<'a> {
    env: JNIEnv<'a>,
    ext_callback: JObject<'a>,
}

impl<'a> Callback<'a> {
    fn new(env: JNIEnv<'a>, ext_callback: JObject<'a>) -> Self {
        Callback {
            env: env,
            ext_callback: ext_callback,
        }
    }

    unsafe extern "system" fn invoke(
        &mut self,
        totalfilesize: i64,
        totalbytestransferred: i64,
        streamsize: i64,
        streambytestransferred: i64,
        _dwstreamnumber: u32,
        _dwcallbackreason: LPPROGRESS_ROUTINE_CALLBACK_REASON,
        _hsourcefile: HANDLE,
        _hdestinationfile: HANDLE,
        _lpdata: *const ::core::ffi::c_void,
    ) -> u32 {
        let arr = [
            JValueGen::Long(totalfilesize),
            JValueGen::Long(totalbytestransferred),
            JValueGen::Long(streamsize),
            JValueGen::Long(streambytestransferred),
        ];
        self.env
            .call_method(&self.callback, "onProgressEvent", "(IIII)V", &arr)
            .expect("Java callback failed");
        return 0;
    }
}

现在,为了访问我在结构上定义的

env
ext_callback
字段,我必须将
&mut self
参数添加到我的调用函数中。我认为这已经破坏了函数签名,因此它不会作为 LPPROGRESS_ROUTINE 工作,但也许不会。

继续努力,我创建了一个方法,该方法将构造我的

Callback
实现并使用指向我的方法的指针调用 CopyFileExA 函数。这是我遇到麻烦的地方。我不知道如何创建指向回调方法的指针,因为它不是静态的:

pub extern "system" fn Java_com_nhbb_util_natives_WindowsCopy_copy<'local>(
    mut env: JNIEnv<'local>,
    _object: JObject<'local>,
    source: JString<'local>,
    dest: JString<'local>,
    flags: jint,
    ext_callback: JObject<'local>,
) {
    let source_jstr: JavaStr = env.get_string(&source).expect("Invalid source string");
    let dest_jstr: JavaStr = env.get_string(&dest).expect("Invalid dest string");

    let source_arr = source_jstr.get_raw();
    let dest_arr = dest_jstr.get_raw();

    let source = source_arr as *const u8;
    let dest = dest_arr as *const u8;

    let flags: u32 = flags.try_into().unwrap();

    let callback = Callback::new(env, ext_callback);

    unsafe {
        CopyFileExA(
            PCSTR(source),
            PCSTR(dest),
            LPPROGRESS_ROUTINE::Some(callback::invoke),
    //                               ^^^^^^^^ use of undeclared crate or module `callback`
            None,
            None,
            flags,
        );
    }
}

我想我只是因为缺乏使用这种新语言的经验而苦苦挣扎。我是否使用

struct
采取了正确的方法?有更好的方法吗?

object rust callback java-native-interface
© www.soinside.com 2019 - 2024. All rights reserved.