I want to call the native Windows CopyFileEx
function from within a Rust program but I am having trouble getting a working example of LPPROGRESS_ROUTINE
. I am a Java programmer learning Rust and the lack of OOP paradigms is a challenge for me. In Java, I would use a class that implemented an interface as my callback. However, it looks like the lpprogressroutine
parameter is a pointer to a function, rather than a polymorphic object. But this should be fine; I can just declare a function and create a pointer to it.
I want the callback function to call a different function with additional arguments, so I created a wrapper struct to contain those additional arguments and the callback function:
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;
}
}
Now in order to access the env
and ext_callback
fields that I defined on the struct, I had to add the &mut self
parameter to my invoke function. I think already this ruins the function signature so it wont work as a LPPROGRESS_ROUTINE, but perhaps not.
Continuing the endeavor, I create a method which will construct my Callback
implementation and invoke the CopyFileExA function with a pointer to my method. This is where I am having trouble. I cannot figure out how to create a pointer to the callback method, since it is not static:
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,
);
}
}
I think I am just struggling from lack of experience working with this new language. Am I taking the correct approach using struct
? Is there a better way to do this?