我正在尝试从 Rust 调用 C++ 对象的虚拟方法。我得到了输出,但执行此方法后,它抛出异常。
Unhandled exception at 0x00000001 in testvirtual.exe: 0xC0000005: Access violation executing location 0x00000001.
这是我的问题的最小重现
#include <iostream>
#include<Windows.h>
struct Calculate{
virtual int sum(int a, int b) {
return a + b;
}
};
extern "C" __declspec(dllexport) Calculate* get_numbers() {
Calculate* obj = new Calculate();
return obj;
}
int main() {
typedef void(func)();
auto handle = LoadLibrary(TEXT("testvirtualrs.dll"));
auto address = (func*)GetProcAddress(handle, "execute");
(*address)();
return 0;
}
铁锈:
#[link(name = "testvirtual")]
extern "C" {
fn get_numbers() -> *mut Calculate;
}
#[repr(C)]
pub struct CalculateVtbl {
pub sum: unsafe extern "C" fn(a: i32, b: i32) -> i32,
}
#[repr(C)]
pub struct Calculate {
pub vtbl: *const CalculateVtbl,
}
impl Calculate {
#[inline(always)]
pub unsafe fn sum(&self, a: i32, b: i32) -> i32 {
((*self.vtbl).sum)(a, b)
}
}
#[no_mangle]
pub unsafe extern "C" fn execute() {
let a = get_numbers();
unsafe {
let val = (*a).sum(5, 6);
println!("val = {val}");
}
}
输出:
val = 11
我还使用 clang 编译器 和 x86 arch。从 Rust 调用方法完成后抛出异常,有点模棱两可,对查找原因没有帮助
在 C++ 中,应该使用
this
指针集来调用非静态方法。名义上,这只是一个隐藏的第一个参数,但在 x86 Windows 上,需要遵循特殊的 thiscall
调用约定。
因此,您需要在 vtable 中创建非常方法 - 以及非虚拟方法 -
extern "thiscall"
:
#[repr(C)]
pub struct CalculateVtbl {
pub sum: unsafe extern "thiscall" fn(this: *mut Calculate, a: i32, b: i32) -> i32,
}
#[repr(C)]
pub struct Calculate {
pub vtbl: *const CalculateVtbl,
}
impl Calculate {
#[inline(always)]
pub unsafe fn sum(&mut self, a: i32, b: i32) -> i32 {
((*self.vtbl).sum)(self, a, b)
}
}
您可以将此示例放入 Compiler Explorer 中,看看如果将
thiscall
更改回 C
,ECX 会发生什么情况。