如何在Rust中实现ArmA 3 DLL的RVExtension函数?

Rid*_*dim 4 c++ ffi rust

我正在尝试为ArmA 3编写DLL扩展,游戏文档说:

dll应包含_RVExtension @ 12格式的入口点,并带有以下C签名:

void __stdcall RVExtension(char *output, int outputSize, const char *function);
Run Code Online (Sandbox Code Playgroud)

C++代码示例的一部分是:

// ...

extern "C" {
    __declspec(dllexport) void __stdcall RVExtension(
        char *output,
        int outputSize,
        const char *function
    ); 
};

void __stdcall RVExtension(
    char *output,
    int outputSize,
    const char *function
) {
    outputSize -= 1;
    strncpy(output,function,outputSize);
}
Run Code Online (Sandbox Code Playgroud)

文档在其他语言中也有很多例子:C#,D甚至是Pascal,但是那些对我没什么帮助,因为我对他们的FFI =没有很好的理解.

我坚持使用以下Rust代码:

#[no_mangle]
pub extern "stdcall" fn RVExtension(
    game_output: *mut c_char,
    output_size: c_int,
    game_input: *const c_char
) {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

但ArmA拒绝称呼它.

Rid*_*dim 5

感谢@ Shepmaster关于Dependency Walker的建议,我发现问题出现在函数名称中.我希望函数名称可以转换成_name@X,但事实并非如此.RVExtension从字面上导出,ArmA无法通过名称找到它_RVExtension@12.

这很奇怪,但似乎编译器版本可能起作用.我尝试了~8个不同的版本,并且能够使它只适用于Rust nightly 1.8(GNU ABI)32位.

工作代码是:

#![feature(libc)]
extern crate libc;

use libc::{strncpy, size_t};

use std::os::raw::c_char;
use std::ffi::{CString, CStr};
use std::str;

#[allow(non_snake_case)]
#[no_mangle]
/// copy the input to the output
pub extern "stdcall" fn _RVExtension(
    response_ptr: *mut c_char,
    response_size: size_t,
    request_ptr: *const c_char,
) {
    // get str from arma
    let utf8_arr: &[u8] = unsafe { CStr::from_ptr(request_ptr).to_bytes() };
    let request: &str = str::from_utf8(utf8_arr).unwrap();

    // send str to arma
    let response: *const c_char = CString::new(request).unwrap().as_ptr();
    unsafe { strncpy(response_ptr, response, response_size) };
}
Run Code Online (Sandbox Code Playgroud)

也可以将函数重写为:

#[export_name="_RVExtension"]
pub extern "stdcall" fn RVExtension(
Run Code Online (Sandbox Code Playgroud)

其他一些Rust编译器也可以使用:

#[export_name="_RVExtension@12"]
pub extern "stdcall" fn RVExtension(
Run Code Online (Sandbox Code Playgroud)

但是,例如,VS 2015的每晚1.8(MSVC ABI)32位将不允许@符号并在编译时抛出错误.MSVC版本不会自行添加@12.

其他编译器可能会添加@12,函数将导出为_RVExtension@12@12.


还值得一提的是,ArmA是32位应用程序,所以它不适用于64位DLL.