首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ACCESS_VIOLATION从Rust调用Btrieve BTRCALL函数

ACCESS_VIOLATION从Rust调用Btrieve BTRCALL函数
EN

Stack Overflow用户
提问于 2020-01-01 16:43:52
回答 1查看 255关注 0票数 1

我正试图从Rust中调用Btrieve (一个非常老的数据库引擎)。这有点长,但这是我第一次尝试FFI从锈,我想描述我所做的一切。

Btrieve引擎在DLL中实现,w3btrv7.dll是32位DLL。我使用32位MSVC工具为它创建了一个导入库(它没有附带正式的):

代码语言:javascript
复制
lib /Def:w3btrv7.def /Out:w3btrv7.lib /Machine:x86

然后,我安装了32位锈蚀工具链stable-i686-pc-windows-msvc,并将其设置为我的默认设置。Bindgen在官方的Btrieve头球上乱翻,所以我不得不自己做。幸运的是,我们只需要包装一个函数,BTRCALL

我的包装纸里有这个

代码语言:javascript
复制
short int BTRCALL(
    unsigned short  operation,
    void*           posBlock,
    void*           dataBuffer,
    unsigned short* dataLength,
    void*           keyBuffer,
    unsigned char   keyLength,
    char            ckeynum);

我的联系是:

代码语言:javascript
复制
println!("cargo:rustc-link-lib=./src/pervasive/w3btrv7");

这似乎是可行的:程序运行,是一个32位的exe,我可以在Process中看到它已经加载了w3btrv7.dll

当我通过bindgen发送标头时,我得到:

代码语言:javascript
复制
extern "C" {
    pub fn BTRCALL(
        operation: ::std::os::raw::c_ushort,
        posBlock: *mut ::std::os::raw::c_void,
        dataBuffer: *mut ::std::os::raw::c_void,
        dataLength: *mut ::std::os::raw::c_ushort,
        keyBuffer: *mut ::std::os::raw::c_void,
        keyLength: ::std::os::raw::c_uchar,
        ckeynum: ::std::os::raw::c_char,
    ) -> ::std::os::raw::c_short;
}

类型和大小似乎都是正确的,它们与我从C#应用程序中获得的一个完美工作的C#相匹配:

代码语言:javascript
复制
[DllImport("w3btrv7.dll", CharSet = CharSet.Ansi)]
private static extern short BTRCALL(
    ushort operation, // In C#, ushort = UInt16.
    [MarshalAs(UnmanagedType.LPArray, SizeConst = 128)] byte[] posBlock,
    [MarshalAs(UnmanagedType.LPArray)] byte[] dataBuffer,
    ref ushort dataLength,
    [MarshalAs(UnmanagedType.LPArray)] byte[] keyBuffer,
    byte keyLength,     // unsigned byte
    char keyNumber);    // 2 byte char

keyNumber略有不同,但我尝试过有符号变体和无符号变体中的字节和空号,但仍然无法工作。

不幸的是,当我运行我的程序时,它在第一次调用BTRCALL之后就会崩溃。(实际上,这个调用是在返回函数时进行的)。我已经将所有参数提取到局部变量中,并检查了它们的类型,并且看起来都是正确的:

代码语言:javascript
复制
let op: u16 = 0;
let mut pos_block: [u8; 128] = self.pos_block.clone();
let pos_block_ptr: *mut std::ffi::c_void = pos_block.as_mut_ptr() as *mut _;
let mut data_buffer: [u8; 32768] = self.data_buffer.clone();
let data_buffer_ptr: *mut std::ffi::c_void = data_buffer.as_mut_ptr() as *mut _;
let mut data_length: u16 = data_buffer.len() as u16;
let mut key_buffer: [u8; 256] = self.key_buffer.clone();
let key_buffer_ptr: *mut std::ffi::c_void = key_buffer.as_mut_ptr() as *mut _;
let key_length: u8 = 255; //self.key_length;
let key_number: i8 = self.key_number.try_into().unwrap();

let status: i16 = BTRCALL(
    op,
    pos_block_ptr,
    data_buffer_ptr,
    &mut data_length,
    key_buffer_ptr,
    key_length,
    key_number
);

它使程序崩溃

代码语言:javascript
复制
error: process didn't exit successfully: `target\debug\blah.exe` (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)

据我所读,这可能是由于不正确的地址访问。

实际上,当我输入一些跟踪来检查变量时,有一些非常有趣的行为,因为被值传递的局部变量似乎被覆盖了。这里的日志只是转储缓冲的前30个字节,因为剩下的只是零:

代码语言:javascript
复制
pos_block = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
pos_block_ptr = 0xad6524
data_buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
data_buffer_ptr = 0xad65a8
data_length = 32768
key_buffer = [34, 67, 58, 92, 116, 101, 109, 112, 92, 99, 115, 115, 92, 120, 100, 98, 92, 67, 65, 83, 69, 46, 68, 66, 34, 0, 0, 0, 0, 0]
key_buffer_ptr = 0xade5b0
key_length = 255
key_number = 0

>>>>>>>>>>>>>>> AFTER THE CALL TO BTRCALL:

pos_block = [0, 0, 0, 0, 0, 0, 0, 0, 0, 76, 203, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0]
pos_block_ptr = 0x0
data_buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
data_buffer_ptr = 0x42442e45
data_length = 0
key_buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
key_buffer_ptr = 0x0
key_length = 173
key_number = 0
BTRCALL() returned B_NO_ERROR

注意,除其他外,pos_block_ptr已设置为0。相反,成功地执行来自C#代码的完全相同的调用,只需将一些数据写入pos_block的前18个字节,而不改变任何其他变量。

好像有点狂暴开始覆盖记忆.

在这一点上,我不知道下一步该尝试什么。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-01-01 17:28:51

将声明从extern "C"更改为extern "stdcall"工作:

代码语言:javascript
复制
extern "stdcall" {
    pub fn BTRCALL(
        operation: ::std::os::raw::c_ushort,
        posBlock: *mut ::std::os::raw::c_void,
        dataBuffer: *mut ::std::os::raw::c_void,
        dataLength: *mut ::std::os::raw::c_ushort,
        keyBuffer: *mut ::std::os::raw::c_void,
        keyLength: ::std::os::raw::c_uchar,
        ckeynum: ::std::os::raw::c_char,
    ) -> ::std::os::raw::c_short;
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59554267

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档