如何在 Rust 中为 ArmA 3 DLL 实现 RVExtension 函数?
How to implement the RVExtension function for an ArmA 3 DLL in Rust?
我正在尝试为 ArmA 3 编写 DLL 扩展,game docs 说:
The dll is expected to contain an entry point of a form
_RVExtension@12, with a following C signature:
void __stdcall RVExtension(char *output, int outputSize, const char *function);
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);
}
文档中还有大量其他语言的示例,例如:C#, D and even Pascal,但这些对我帮助不大,因为我不太了解他们的 FFI =(.
我受困于以下 Rust 代码:
#[no_mangle]
pub extern "stdcall" fn RVExtension(
game_output: *mut c_char,
output_size: c_int,
game_input: *const c_char
) {
// ...
}
但是 ArmA 拒绝调用它。
感谢@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) };
}
也可以把函数改写成:
#[export_name="_RVExtension"]
pub extern "stdcall" fn RVExtension(
其他一些 Rust 编译器也可以使用:
#[export_name="_RVExtension@12"]
pub extern "stdcall" fn RVExtension(
但是,例如,带有 VS 2015 的 nightly 1.8 (MSVC ABI) 32 位将不允许 @
符号并在编译时抛出错误。 MSVC版本不会自己加@12
其他编译器可能会添加 @12
,函数将导出为 _RVExtension@12@12
。
还值得一提的是,ArmA 是 32 位应用程序,因此它不适用于 64 位 DLL。
我正在尝试为 ArmA 3 编写 DLL 扩展,game docs 说:
The dll is expected to contain an entry point of a form _RVExtension@12, with a following C signature:
void __stdcall RVExtension(char *output, int outputSize, const char *function);
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);
}
文档中还有大量其他语言的示例,例如:C#, D and even Pascal,但这些对我帮助不大,因为我不太了解他们的 FFI =(.
我受困于以下 Rust 代码:
#[no_mangle]
pub extern "stdcall" fn RVExtension(
game_output: *mut c_char,
output_size: c_int,
game_input: *const c_char
) {
// ...
}
但是 ArmA 拒绝调用它。
感谢@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) };
}
也可以把函数改写成:
#[export_name="_RVExtension"]
pub extern "stdcall" fn RVExtension(
其他一些 Rust 编译器也可以使用:
#[export_name="_RVExtension@12"]
pub extern "stdcall" fn RVExtension(
但是,例如,带有 VS 2015 的 nightly 1.8 (MSVC ABI) 32 位将不允许 @
符号并在编译时抛出错误。 MSVC版本不会自己加@12
其他编译器可能会添加 @12
,函数将导出为 _RVExtension@12@12
。
还值得一提的是,ArmA 是 32 位应用程序,因此它不适用于 64 位 DLL。