windowswinapirustffitrampolines

How do I safely wrap a callback to pass to Windows FFI?


I am trying to write a wrapper on top of winapi. I want to wrap functions that accept pointers for callback functions.

As an example, consider this:

// The unsafe callback type the FFI function accepts
type UnsafeCallback = unsafe extern "system" fn(exception_info: *mut ExceptionInfo) -> u32;

// The safe callback type my function should accept
type SafeCallback = fn(exception_info: &ConvertedExceptionInfo) -> u32;

The functions that will be used:

// The function exposed by winapi
unsafe extern "system" fn SetExceptionHandler(handler: UnsafeCallback);

// The function I want to expose in my library
fn SetExceptionHandler(handler: SafeCallback);

I want to create a wrapping function that looks like this:

unsafe extern "system" fn(exception_info: *mut ExceptionInfo) -> u32 {
    let result = panic::catch_unwind(|| {
        // Convert ExceptionInfo into ConvertedExceptionInfo. I know this is undefined behavior, but its only here
        // to demonstrate program flow
        let converted_exception_info: ConvertedExceptionInfo = (*exception_info).into();
        
        // Call the corresponding safe function (as to how we get the function pointer here, that's 
        // the whole question)
        return safe_callback(&converted_exception_info);
    });

    return match result {
        Ok(val) => val,
        Err(_) => _
    };
}

I can think of two possibilities to create this wrapping function:

  1. Creating a wrapping function at runtime

    Create a closure or similar construct inside the safe SetExceptionHandler method.

    I have no idea how to get the closure across the FFI boundary.

  2. Exposing a conversion macro and generating the function at compile time

    Edit the SetExceptionHandler function to accept the UnsafeCallback type.

    Then I could create a macro that generates the wrapping function at compile time and expose this macro to the user.

    I would have to expose unsafe extern parameters again, so it is not how I would prefer to do it.

    I have no idea how to structure such a macro or if this is even possible.


Is my first idea possible and feasible? If so, how could this be done? If not, is writing a macro like the second idea possible and feasible? If so, how could this be done?

Based on

I get the impression that my first idea is probably not possible with the exception of something called trampolining.

Is trampolining possible in safe Rust and in this situation?


Solution

  • After much searching, i have found a blog post that explains a nice solution for the problem of wrapping callbacks. Article here