1

Rust から Btrieve (非常に古いデータベース エンジン) を呼び出そうとしています。少し長くなりますが、Rust から FFI を試すのはこれが初めてです。

Btrieve エンジンは、32 ビット DLL である w3btrv7.dll という DLL に実装されています。32ビットのMSVCツールを使用してインポートライブラリを作成しました(公式のものは付属していません):

lib /Def:w3btrv7.def /Out:w3btrv7.lib /Machine:x86

次に、32 ビットの Rust ツールチェーンstable-i686-pc-windows-msvc をインストールし、それをデフォルトとして設定しました。公式の Btrieve ヘッダーに Bindgen バーフがあるため、自分で作成する必要がありました。幸いなことに、ラップする必要がある関数は 1 つだけです BTRCALL

私はこれを私のwrapper.hに持っています:

short int BTRCALL(
    unsigned short  operation,
    void*           posBlock,
    void*           dataBuffer,
    unsigned short* dataLength,
    void*           keyBuffer,
    unsigned char   keyLength,
    char            ckeynum);

私は次のようにリンクしています:

println!("cargo:rustc-link-lib=./src/pervasive/w3btrv7");

これは機能しているようです: プログラムが実行され、32 ビットの exe であり、Process Explorer でそれが読み込まれたことを確認できますw3btrv7.dll

bindgen を介してヘッダーを送信すると、次のようになります。

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# アプリケーションから取得した DllImport と一致します。

[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 への最初の呼び出しの後に爆発します。(まあ、実際には、この呼び出しが含まれている関数が返されるときです)。すべてのパラメーターをローカル変数に抽出し、それらのタイプをチェックしたところ、すべてが正しいように見えました。

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
);

それはプログラムをクラッシュさせます

error: process didn't exit successfully: `target\debug\blah.exe` (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)

私が読んだことから、これはおそらく不適切なアドレスアクセスによるものです。

実際、変数をチェックするためにいくつかのトレースを入れると、値によって渡されるローカル変数が上書きされているように見えるという点で、いくつかの非常に興味深い動作があります。ここのログは、バッファの最初の 30 バイトだけをダンプします。残りはゼロだからです。

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# コードからまったく同じ呼び出しを正常に実行すると、データが の最初の 18 バイトに書き込まれるだけpos_block で、他の変数は変更されません。

少し凶暴になり、メモリを上書きし始めたようです...

この時点で、次に何を試すべきかわかりません。

4

1 に答える 1